WebCore:
authorbeidson <beidson@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 8 Sep 2007 20:33:08 +0000 (20:33 +0000)
committerbeidson <beidson@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 8 Sep 2007 20:33:08 +0000 (20:33 +0000)
        Reviewed by Darin

        <rdar://problem/5434431> - Asynchronous Icon Database

        The IconDatabase API was originally designed to be fully asynchronous - if an icon wasn't read in from disk
        when you asked for it, you would be notified when it was.

        Safari 2 did writes on a background thread, but reads blocked the main thread.

        The current WebCore implementation using SQLite attempted to get rid of the background thread by defering expensive
        writes via timers, but falls short in moderate to extreme usage cases

        Time to make the IconDatabase live up to it's fully asynchronous destiny.

        This should -
        - Make the browser instantly usable while converting Safari 2 icons in the background occurs
        - Remedy any UI slowness/blocking when on slow network home directories
        - Remedy random UI slowness, pauses, and stutters do to random I/O occurring at the exact wrong time or under heavy
          disk usage from swapping or other apps on the system
        - Allow certain long-running procedures to be interruptible (Safari 2 import, reading icons in from disk when trying to quit, etc)

        This will have a noticeable effect on current Safari 2 and Safari 3 beta browsers, including icons not appearing in bookmarks, history,
        or the location field the first time they're asked for, as current released Safari's don't properly listen for these async notifations.
        The second time such a menu or view is brought up, the icon should be there.

        Additionally this includes a SQLite schema change which will be a lot more efficient but will result in the loss of current SQLite icons.
        Converting from Safari 2 style icons will still work.

        WebCore, welcome to multi-threadedness

        * WebCore.exp:
        * WebCore.xcodeproj/project.pbxproj:
        * WebCore.vcproj/WebCore.vcproj:

        * loader/DocumentLoader.cpp:
        (WebCore::DocumentLoader::iconLoadDecisionAvailable): Called when an Icon becomes available that was requested by this
          DocumentLoader (to support the webView:didReceiveIcon: delegate call in WebKit)
        * loader/DocumentLoader.h:

        * loader/FrameLoader.cpp:
        (WebCore::FrameLoader::iconLoadDecisionAvailable): Called from the DocumentLoaders who get notified - if the FrameLoader
          ends up not caring because the WebView has transitioned to a new page, nothing occurs.  Otherwise, the FrameLoader possibly
          starts it Icon Loader and possibly sends the webView:didReceiveIcon: delegate call
        (WebCore::FrameLoader::startIconLoader): Instead of "Yes, load the icon now" or "No, don't load it" there is a third possibility -
          "You might be asked to load your icon later."  Add supporting logic for receiving this state, and being called a second time
          when the load decision is finally available.
        * loader/FrameLoader.h:

        * loader/FrameLoaderClient.h: Added "registerForIconNotification" which is a way to tell WebViews "The icon you are interested in might
          become available via the generic WebIconDatabaseDidAddIconNotification instead of a targeted delegate call"
          A WebView can then receive the generic notification and pass on it's own targeted delegate call.

        * loader/icon/IconDataCache.cpp: Removed.
        * loader/icon/IconDataCache.h: Removed.

        * loader/icon/IconDatabase.cpp:
        (WebCore::urlForLogging): Cut a URL down in length for sane logging and debugging
        (WebCore::defaultClient): Return the default, empty IconDatabaseClient incase the API doesn't set one.

        Following block of methods are for the Main thread's usage -
        (WebCore::IconDatabase::setClient):
        (WebCore::makeAllDirectories): Small optimization that checks to see if the entire path exists already, and doesn't try to loop
          through each patch component if the full path is already present
        (WebCore::IconDatabase::open): Makes all directories to the target path and kicks off the background thread - nothing more.
        (WebCore::IconDatabase::close): Signals the thread to quit and waits for it to do so
        (WebCore::IconDatabase::removeAllIcons): Purge the icon database
        (WebCore::IconDatabase::iconForPageURL):
        (WebCore::IconDatabase::readIconForPageURLFromDisk):
        (WebCore::IconDatabase::iconURLForPageURL):
        (WebCore::IconDatabase::defaultIcon):
        (WebCore::IconDatabase::retainIconForPageURL):
        (WebCore::IconDatabase::releaseIconForPageURL):
        (WebCore::IconDatabase::setIconDataForIconURL):
        (WebCore::IconDatabase::setIconURLForPageURL):
        (WebCore::IconDatabase::loadDecisionForIconURL): Determine if an icon loader should load now.  If the decision is "maybe later", then
          mark the DocumentLoader to be notified later when the final decision is available.
        (WebCore::IconDatabase::iconDataKnownForIconURL): Determine if the actual image data has been read from disk (or set from the loader) for
          icon URL in question
        (WebCore::IconDatabase::setEnabled):
        (WebCore::IconDatabase::isEnabled):
        (WebCore::IconDatabase::setPrivateBrowsingEnabled):
        (WebCore::IconDatabase::isPrivateBrowsingEnabled):
        (WebCore::IconDatabase::delayDatabaseCleanup): Restore this method from a year ago, as asynchronous pruning of icons can now occur on a
          background thread.
        (WebCore::IconDatabase::allowDatabaseCleanup):
        (WebCore::IconDatabase::checkIntegrityBeforeOpening):
        (WebCore::IconDatabase::pageURLMappingCount):
        (WebCore::IconDatabase::retainedPageURLCount):
        (WebCore::IconDatabase::iconRecordCount):
        (WebCore::IconDatabase::iconRecordCountWithData):
        (WebCore::IconDatabase::IconDatabase):
        (WebCore::IconDatabase::~IconDatabase):
        (WebCore::IconDatabase::notifyPendingLoadDecisions): Tell all the registered DocumentLoaders "Hey, we've read in all URL mappings from disk,
          so check to see if you are interested in any of them"
        (WebCore::IconDatabase::notifyPendingLoadDecisionsInternal):
        (WebCore::IconDatabase::wakeSyncThread): Wake the sync thread, if it is idle
        (WebCore::IconDatabase::scheduleOrDeferSyncTimer): Even though we're on a background thread, we still defer writing out to disk during
          periods of high activity
        (WebCore::IconDatabase::syncTimerFired): Call wakeSyncThread()

        Following block of methods may be used by either thread -
        (WebCore::IconDatabase::isOpen):
        (WebCore::IconDatabase::databasePath):
        (WebCore::IconDatabase::defaultDatabaseFilename):
        (WebCore::IconDatabase::getOrCreateIconRecord):
        (WebCore::IconDatabase::getOrCreatePageURLRecord):

        Following block of methods are used by the secondary thread only -
        (WebCore::IconDatabase::importIconURLForPageURL): For the Safari 2 import procedure - write a URL mapping directly out to disk
        (WebCore::IconDatabase::importIconDataForIconURL): For the Safari 2 import procedure - write an Icon directly out to disk
        (WebCore::IconDatabase::shouldStopThreadActivity): To check and see if the thread should stop what it is doing now to do something
          more important (such as quit, or delete all icons)
        (WebCore::IconDatabase::iconDatabaseSyncThreadStart):
        (WebCore::IconDatabase::iconDatabaseSyncThread): Entry point for the background thread
        (WebCore::databaseVersionNumber):
        (WebCore::isValidDatabase):
        (WebCore::createDatabaseTables):
        (WebCore::IconDatabase::performOpenInitialization): Open and validate the SQLite database, making sure it's schema jives with what
          is expected
        (WebCore::IconDatabase::checkIntegrity):
        (WebCore::IconDatabase::performURLImport): Import all the Page URL -> Icon URL mappings from the database.  Done "1st thing" on startup,
          this is necessary to be able to give the loader decisions about whether or not it should load icons from the network
        (WebCore::IconDatabase::syncThreadMainLoop): Main loop - sleeps until woken up, then does a read cycle and a write cycle until both cycles
          do no work - then it goes back to sleep.
        (WebCore::IconDatabase::readFromDatabase): Reads icons from the database that clients are waiting on
        (WebCore::IconDatabase::writeToDatabase): Writes any changes page -> icon url mappings to disk, as well as any new image data that has
          been received from the loader
        (WebCore::IconDatabase::pruneUnretainedIcons): Done only once, and only after the first write to the database, this procedure removes all
          icons and page URLs from disk that haven't been retained by any client.  Note that the prune can be delayed by utilizing delayDatabaseCleanup()
        (WebCore::IconDatabase::checkForDanglingPageURLs): Usually part of the prune procedure, prunes any pages who point to icons that no longer exist
          in the database
        (WebCore::IconDatabase::removeAllIconsOnThread): Completely purge both the on-disk and in memory records of all icons
        (WebCore::IconDatabase::deleteAllPreparedStatements): Part of removeAllIcons and the thread cleanup procedure
        (WebCore::IconDatabase::cleanupSyncThread): Write out any last remaining writes to disk, close the database, and then end the thread
        (WebCore::IconDatabase::imported): Checks the DB to see if the Safari 2 import has occured
        (WebCore::IconDatabase::setImported): Sets the "Safari 2 imported" flag
        (WebCore::readySQLStatement):
        (WebCore::IconDatabase::setIconURLForPageURLInSQLDatabase): This and the following "SQLDatabase" suffixed methods are pretty self explanatory
        (WebCore::IconDatabase::setIconIDForPageURLInSQLDatabase):
        (WebCore::IconDatabase::removePageURLFromSQLDatabase):
        (WebCore::IconDatabase::getIconIDForIconURLFromSQLDatabase):
        (WebCore::IconDatabase::addIconURLToSQLDatabase):
        (WebCore::IconDatabase::getImageDataForIconURLFromSQLDatabase):
        (WebCore::IconDatabase::removeIconFromSQLDatabase):
        (WebCore::IconDatabase::writeIconSnapshotToSQLDatabase):
        * loader/icon/IconDatabase.h:

        * loader/icon/IconDatabaseClient.h: Added.
        (WebCore::IconDatabaseClient::~IconDatabaseClient):
        (WebCore::IconDatabaseClient::performImport): Perform the Safari 2 import, implemented by WebKit
        (WebCore::IconDatabaseClient::dispatchDidRemoveAllIcons): Send the API notification
        (WebCore::IconDatabaseClient::dispatchDidAddIconForPageURL): Ditto

        * loader/icon/IconDatabaseNone.cpp: Best attempt to keep non icon-DB platforms building
        (WebCore::IconDatabase::defaultDatabaseFilename):
        (WebCore::IconDatabase::readIconForPageURLFromDisk):
        (WebCore::IconDatabase::loadDecisionForIconURL):
        (WebCore::IconDatabase::iconDataKnownForIconURL):
        (WebCore::IconDatabase::setIconURLForPageURL):
        (WebCore::IconDatabase::isEnabled):
        (WebCore::IconDatabase::delayDatabaseCleanup):
        (WebCore::IconDatabase::allowDatabaseCleanup):
        (WebCore::IconDatabase::setClient):

        * loader/icon/IconRecord.cpp: Added.
        (WebCore::IconRecord::IconRecord): IconRecord used to be "IconDataCache" - it is merely a container for the url, timestamp, and image for a site icon.
          It is Shared, and therefore ref counted - PageURLRecords are the owning containers.  This is a tricky way to track how many page urls are retaining
          an IconRecord and therefore tracking when we should try to get rid of one.
        (WebCore::IconRecord::~IconRecord):
        (WebCore::IconRecord::image):
        (WebCore::IconRecord::setImageData):
        (WebCore::IconRecord::loadImageFromResource):
        (WebCore::IconRecord::imageDataStatus): Return whether the image data hasn't been read yet, exists in memory, or is absent (site with no icon)
        (WebCore::IconRecord::snapshot): Returns a snapshot of the icon's data - url, timestamp, and image data - to be written to disk
        * loader/icon/IconRecord.h: Added.
        (WebCore::IconSnapshot::IconSnapshot):
        (WebCore::IconRecord::getTimestamp):
        (WebCore::IconRecord::setTimestamp):
        (WebCore::IconRecord::iconURL):
        (WebCore::IconRecord::retainingPageURLs):

        * loader/icon/PageURLRecord.cpp: Added.
        (WebCore::PageURLRecord::PageURLRecord): PageURLRecord is fundamentally a pairing of a Page URL to an Icon.  It has manual ref counting for the sake
          of "retainIconForPageURL" and "releaseIconForPageURL", and can provide a quick snapshot of it's Page URL -> Icon URL mapping for writing to
          the database
        (WebCore::PageURLRecord::setIconRecord):
        (WebCore::PageURLRecord::snapshot):
        * loader/icon/PageURLRecord.h: Added.
        (WebCore::PageURLSnapshot::PageURLSnapshot):
        (WebCore::PageURLRecord::url):
        (WebCore::PageURLRecord::PageURLRecord::iconRecord):
        (WebCore::PageURLRecord::retain):
        (WebCore::PageURLRecord::release):
        (WebCore::PageURLRecord::retainCount):

        * platform/SharedBuffer.cpp:
        (WebCore::SharedBuffer::copy): Added a deep copy method for the purposes of handing icon data across the thread boundary into the icon database
        * platform/SharedBuffer.h:

        * platform/graphics/svg/SVGImageEmptyClients.h:
        (WebCore::SVGEmptyFrameLoaderClient::registerForIconNotification):

        * platform/win/TemporaryLinkStubs.cpp:
        (WebCore::callOnMainThread): Only other IconDatabase utilizing platform - keep their build going

WebKit:

        Reviewed by Darin

        <rdar://problem/5434431> - Asynchronous Icon Database

        WebKit side of things
        Mainly, there are Notifications WebKit has to listen for now that tell it when to either call back into WebCore
        for some purpose or to send the webView:didReceiveIcon: delegate call

        Many smaller tweaks as well.

        * Misc/WebIconDatabase.h:
        * Misc/WebIconDatabase.mm:
        (defaultClient):
        (-[WebIconDatabase init]):
        (+[WebIconDatabase delayDatabaseCleanup]): Accessor so clients can prevent the thread from cleaning up the database
          before they've done all their necessary retaining of icons.
        (+[WebIconDatabase allowDatabaseCleanup]):
        (-[WebIconDatabase removeAllIcons]):
        (-[WebIconDatabase _isEnabled]):
        (-[WebIconDatabase _sendNotificationForURL:]):
        (-[WebIconDatabase _sendDidRemoveAllIconsNotification]):
        (-[WebIconDatabase _databaseDirectory]):

        (-[ThreadEnabler threadEnablingSelector:]): Quick and dirty class to enabled Cocoa multithreading
        (+[ThreadEnabler enableThreading]):
        (importToWebCoreFormat):
        * Misc/WebIconDatabaseInternal.h: Expose the internal methods of WebIconDatabase that are required by WebIconDatabaseClient

        * Misc/WebNSNotificationCenterExtras.h: Added. - Great utility class whose design was borrowed from Colloquy
          that allows the posting of a Cocoa notification on the main thread from *any* thread
        * Misc/WebNSNotificationCenterExtras.m: Added.
        (-[NSNotificationCenter postNotificationOnMainThreadWithName:object:]):
        (-[NSNotificationCenter postNotificationOnMainThreadWithName:object:userInfo:]):
        (-[NSNotificationCenter postNotificationOnMainThreadWithName:object:userInfo:waitUntilDone:]):
        (+[NSNotificationCenter _postNotificationName:]):

        * WebCoreSupport/WebFrameLoaderClient.h:
        * WebCoreSupport/WebFrameLoaderClient.mm:
        (WebFrameLoaderClient::dispatchDidReceiveIcon): Send the webView:didReceiveIcon: delegate call
        (WebFrameLoaderClient::registerForIconNotification):

        * WebCoreSupport/WebIconDatabaseClient.h: Added.
        * WebCoreSupport/WebIconDatabaseClient.mm: Added.
        (WebIconDatabaseClient::performImport):  Perform the Safari 2 icon import
        (WebIconDatabaseClient::dispatchDidRemoveAllIcons): Send the NSNotification
        (WebIconDatabaseClient::dispatchDidAddIconForPageURL): Ditto

        * WebView/WebView.mm:
        (-[WebView _receivedIconChangedNotification:]): Check and see if this notification is for this WebView's current URL by
          calling back into the IconDatabase
        (-[WebView _registerForIconNotification:]): Support for WebIconDatabaseClient
        (-[WebView _dispatchDidReceiveIconFromWebFrame:]): Dispatch this delegate call as well as unregister for the notification
        * WebView/WebViewInternal.h:

        * WebKit.xcodeproj/project.pbxproj:

win:

        <rdar://problem/5434431> - Asynchronous Icon Database

        * WebFrame.cpp:
        (WebFrame::didPerformFirstNavigation): Empty impl for now
        (WebFrame::registerForIconNotification): Ditto
        * WebFrame.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@25439 268f45cc-cd09-0410-ab3c-d52691b4dbfc

39 files changed:
WebCore/ChangeLog
WebCore/WebCore.exp
WebCore/WebCore.vcproj/WebCore.vcproj
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/loader/DocumentLoader.cpp
WebCore/loader/DocumentLoader.h
WebCore/loader/FrameLoader.cpp
WebCore/loader/FrameLoader.h
WebCore/loader/FrameLoaderClient.h
WebCore/loader/icon/IconDataCache.cpp [deleted file]
WebCore/loader/icon/IconDataCache.h [deleted file]
WebCore/loader/icon/IconDatabase.cpp
WebCore/loader/icon/IconDatabase.h
WebCore/loader/icon/IconDatabaseClient.h [new file with mode: 0644]
WebCore/loader/icon/IconDatabaseNone.cpp
WebCore/loader/icon/IconRecord.cpp [new file with mode: 0644]
WebCore/loader/icon/IconRecord.h [new file with mode: 0644]
WebCore/loader/icon/PageURLRecord.cpp [new file with mode: 0644]
WebCore/loader/icon/PageURLRecord.h [new file with mode: 0644]
WebCore/platform/SharedBuffer.cpp
WebCore/platform/SharedBuffer.h
WebCore/platform/graphics/svg/SVGImageEmptyClients.h
WebCore/platform/win/TemporaryLinkStubs.cpp
WebKit/ChangeLog
WebKit/Misc/WebIconDatabase.h
WebKit/Misc/WebIconDatabase.mm
WebKit/Misc/WebIconDatabaseInternal.h
WebKit/Misc/WebNSNotificationCenterExtras.h [new file with mode: 0644]
WebKit/Misc/WebNSNotificationCenterExtras.m [new file with mode: 0644]
WebKit/WebCoreSupport/WebFrameLoaderClient.h
WebKit/WebCoreSupport/WebFrameLoaderClient.mm
WebKit/WebCoreSupport/WebIconDatabaseClient.h [new file with mode: 0644]
WebKit/WebCoreSupport/WebIconDatabaseClient.mm [new file with mode: 0644]
WebKit/WebKit.xcodeproj/project.pbxproj
WebKit/WebView/WebView.mm
WebKit/WebView/WebViewInternal.h
WebKit/win/ChangeLog
WebKit/win/WebFrame.cpp
WebKit/win/WebFrame.h

index dd5b23de5db6bac3022100a5eff1c3769e77c86c..7a425747ebc5a9867243f6a97c67429b4a585e87 100644 (file)
@@ -1,3 +1,210 @@
+2007-09-08  Brady Eidson  <beidson@apple.com>
+
+        Reviewed by Darin
+
+        <rdar://problem/5434431> - Asynchronous Icon Database
+
+        The IconDatabase API was originally designed to be fully asynchronous - if an icon wasn't read in from disk
+        when you asked for it, you would be notified when it was.
+
+        Safari 2 did writes on a background thread, but reads blocked the main thread.
+
+        The current WebCore implementation using SQLite attempted to get rid of the background thread by defering expensive 
+        writes via timers, but falls short in moderate to extreme usage cases
+
+        Time to make the IconDatabase live up to it's fully asynchronous destiny.
+
+        This should -
+        - Make the browser instantly usable while converting Safari 2 icons in the background occurs
+        - Remedy any UI slowness/blocking when on slow network home directories
+        - Remedy random UI slowness, pauses, and stutters do to random I/O occurring at the exact wrong time or under heavy
+          disk usage from swapping or other apps on the system
+        - Allow certain long-running procedures to be interruptible (Safari 2 import, reading icons in from disk when trying to quit, etc)
+
+        This will have a noticeable effect on current Safari 2 and Safari 3 beta browsers, including icons not appearing in bookmarks, history,
+        or the location field the first time they're asked for, as current released Safari's don't properly listen for these async notifations.  
+        The second time such a menu or view is brought up, the icon should be there.
+
+        Additionally this includes a SQLite schema change which will be a lot more efficient but will result in the loss of current SQLite icons.
+        Converting from Safari 2 style icons will still work.
+
+        WebCore, welcome to multi-threadedness
+
+        * WebCore.exp:
+        * WebCore.xcodeproj/project.pbxproj:
+        * WebCore.vcproj/WebCore.vcproj:
+
+        * loader/DocumentLoader.cpp:
+        (WebCore::DocumentLoader::iconLoadDecisionAvailable): Called when an Icon becomes available that was requested by this 
+          DocumentLoader (to support the webView:didReceiveIcon: delegate call in WebKit)
+        * loader/DocumentLoader.h:
+
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::iconLoadDecisionAvailable): Called from the DocumentLoaders who get notified - if the FrameLoader 
+          ends up not caring because the WebView has transitioned to a new page, nothing occurs.  Otherwise, the FrameLoader possibly
+          starts it Icon Loader and possibly sends the webView:didReceiveIcon: delegate call
+        (WebCore::FrameLoader::startIconLoader): Instead of "Yes, load the icon now" or "No, don't load it" there is a third possibility -
+          "You might be asked to load your icon later."  Add supporting logic for receiving this state, and being called a second time
+          when the load decision is finally available.
+        * loader/FrameLoader.h:
+
+        * loader/FrameLoaderClient.h: Added "registerForIconNotification" which is a way to tell WebViews "The icon you are interested in might
+          become available via the generic WebIconDatabaseDidAddIconNotification instead of a targeted delegate call"
+          A WebView can then receive the generic notification and pass on it's own targeted delegate call.
+
+        * loader/icon/IconDataCache.cpp: Removed.
+        * loader/icon/IconDataCache.h: Removed.
+
+        * loader/icon/IconDatabase.cpp:
+        (WebCore::urlForLogging): Cut a URL down in length for sane logging and debugging
+        (WebCore::defaultClient): Return the default, empty IconDatabaseClient incase the API doesn't set one.
+
+        Following block of methods are for the Main thread's usage -
+        (WebCore::IconDatabase::setClient):
+        (WebCore::makeAllDirectories): Small optimization that checks to see if the entire path exists already, and doesn't try to loop
+          through each patch component if the full path is already present
+        (WebCore::IconDatabase::open): Makes all directories to the target path and kicks off the background thread - nothing more.
+        (WebCore::IconDatabase::close): Signals the thread to quit and waits for it to do so
+        (WebCore::IconDatabase::removeAllIcons): Purge the icon database
+        (WebCore::IconDatabase::iconForPageURL):
+        (WebCore::IconDatabase::readIconForPageURLFromDisk):
+        (WebCore::IconDatabase::iconURLForPageURL):
+        (WebCore::IconDatabase::defaultIcon):
+        (WebCore::IconDatabase::retainIconForPageURL):
+        (WebCore::IconDatabase::releaseIconForPageURL):
+        (WebCore::IconDatabase::setIconDataForIconURL):
+        (WebCore::IconDatabase::setIconURLForPageURL):
+        (WebCore::IconDatabase::loadDecisionForIconURL): Determine if an icon loader should load now.  If the decision is "maybe later", then
+          mark the DocumentLoader to be notified later when the final decision is available.
+        (WebCore::IconDatabase::iconDataKnownForIconURL): Determine if the actual image data has been read from disk (or set from the loader) for 
+          icon URL in question
+        (WebCore::IconDatabase::setEnabled):
+        (WebCore::IconDatabase::isEnabled):
+        (WebCore::IconDatabase::setPrivateBrowsingEnabled):
+        (WebCore::IconDatabase::isPrivateBrowsingEnabled):
+        (WebCore::IconDatabase::delayDatabaseCleanup): Restore this method from a year ago, as asynchronous pruning of icons can now occur on a 
+          background thread.
+        (WebCore::IconDatabase::allowDatabaseCleanup):
+        (WebCore::IconDatabase::checkIntegrityBeforeOpening):
+        (WebCore::IconDatabase::pageURLMappingCount):
+        (WebCore::IconDatabase::retainedPageURLCount):
+        (WebCore::IconDatabase::iconRecordCount):
+        (WebCore::IconDatabase::iconRecordCountWithData):
+        (WebCore::IconDatabase::IconDatabase):
+        (WebCore::IconDatabase::~IconDatabase):
+        (WebCore::IconDatabase::notifyPendingLoadDecisions): Tell all the registered DocumentLoaders "Hey, we've read in all URL mappings from disk,
+          so check to see if you are interested in any of them"
+        (WebCore::IconDatabase::notifyPendingLoadDecisionsInternal):
+        (WebCore::IconDatabase::wakeSyncThread): Wake the sync thread, if it is idle
+        (WebCore::IconDatabase::scheduleOrDeferSyncTimer): Even though we're on a background thread, we still defer writing out to disk during
+          periods of high activity
+        (WebCore::IconDatabase::syncTimerFired): Call wakeSyncThread()
+
+        Following block of methods may be used by either thread -
+        (WebCore::IconDatabase::isOpen):
+        (WebCore::IconDatabase::databasePath):
+        (WebCore::IconDatabase::defaultDatabaseFilename):
+        (WebCore::IconDatabase::getOrCreateIconRecord):
+        (WebCore::IconDatabase::getOrCreatePageURLRecord):
+
+        Following block of methods are used by the secondary thread only -
+        (WebCore::IconDatabase::importIconURLForPageURL): For the Safari 2 import procedure - write a URL mapping directly out to disk
+        (WebCore::IconDatabase::importIconDataForIconURL): For the Safari 2 import procedure - write an Icon directly out to disk
+        (WebCore::IconDatabase::shouldStopThreadActivity): To check and see if the thread should stop what it is doing now to do something
+          more important (such as quit, or delete all icons)
+        (WebCore::IconDatabase::iconDatabaseSyncThreadStart):
+        (WebCore::IconDatabase::iconDatabaseSyncThread): Entry point for the background thread
+        (WebCore::databaseVersionNumber):
+        (WebCore::isValidDatabase):
+        (WebCore::createDatabaseTables):
+        (WebCore::IconDatabase::performOpenInitialization): Open and validate the SQLite database, making sure it's schema jives with what
+          is expected
+        (WebCore::IconDatabase::checkIntegrity):
+        (WebCore::IconDatabase::performURLImport): Import all the Page URL -> Icon URL mappings from the database.  Done "1st thing" on startup,
+          this is necessary to be able to give the loader decisions about whether or not it should load icons from the network
+        (WebCore::IconDatabase::syncThreadMainLoop): Main loop - sleeps until woken up, then does a read cycle and a write cycle until both cycles
+          do no work - then it goes back to sleep.
+        (WebCore::IconDatabase::readFromDatabase): Reads icons from the database that clients are waiting on
+        (WebCore::IconDatabase::writeToDatabase): Writes any changes page -> icon url mappings to disk, as well as any new image data that has 
+          been received from the loader
+        (WebCore::IconDatabase::pruneUnretainedIcons): Done only once, and only after the first write to the database, this procedure removes all
+          icons and page URLs from disk that haven't been retained by any client.  Note that the prune can be delayed by utilizing delayDatabaseCleanup()
+        (WebCore::IconDatabase::checkForDanglingPageURLs): Usually part of the prune procedure, prunes any pages who point to icons that no longer exist 
+          in the database
+        (WebCore::IconDatabase::removeAllIconsOnThread): Completely purge both the on-disk and in memory records of all icons
+        (WebCore::IconDatabase::deleteAllPreparedStatements): Part of removeAllIcons and the thread cleanup procedure
+        (WebCore::IconDatabase::cleanupSyncThread): Write out any last remaining writes to disk, close the database, and then end the thread
+        (WebCore::IconDatabase::imported): Checks the DB to see if the Safari 2 import has occured
+        (WebCore::IconDatabase::setImported): Sets the "Safari 2 imported" flag
+        (WebCore::readySQLStatement):
+        (WebCore::IconDatabase::setIconURLForPageURLInSQLDatabase): This and the following "SQLDatabase" suffixed methods are pretty self explanatory
+        (WebCore::IconDatabase::setIconIDForPageURLInSQLDatabase):
+        (WebCore::IconDatabase::removePageURLFromSQLDatabase):
+        (WebCore::IconDatabase::getIconIDForIconURLFromSQLDatabase):
+        (WebCore::IconDatabase::addIconURLToSQLDatabase):
+        (WebCore::IconDatabase::getImageDataForIconURLFromSQLDatabase):
+        (WebCore::IconDatabase::removeIconFromSQLDatabase):
+        (WebCore::IconDatabase::writeIconSnapshotToSQLDatabase):
+        * loader/icon/IconDatabase.h:
+
+        * loader/icon/IconDatabaseClient.h: Added.
+        (WebCore::IconDatabaseClient::~IconDatabaseClient):
+        (WebCore::IconDatabaseClient::performImport): Perform the Safari 2 import, implemented by WebKit
+        (WebCore::IconDatabaseClient::dispatchDidRemoveAllIcons): Send the API notification
+        (WebCore::IconDatabaseClient::dispatchDidAddIconForPageURL): Ditto
+
+        * loader/icon/IconDatabaseNone.cpp: Best attempt to keep non icon-DB platforms building
+        (WebCore::IconDatabase::defaultDatabaseFilename):
+        (WebCore::IconDatabase::readIconForPageURLFromDisk):
+        (WebCore::IconDatabase::loadDecisionForIconURL):
+        (WebCore::IconDatabase::iconDataKnownForIconURL):
+        (WebCore::IconDatabase::setIconURLForPageURL):
+        (WebCore::IconDatabase::isEnabled):
+        (WebCore::IconDatabase::delayDatabaseCleanup):
+        (WebCore::IconDatabase::allowDatabaseCleanup):
+        (WebCore::IconDatabase::setClient):
+
+        * loader/icon/IconRecord.cpp: Added.
+        (WebCore::IconRecord::IconRecord): IconRecord used to be "IconDataCache" - it is merely a container for the url, timestamp, and image for a site icon.
+          It is Shared, and therefore ref counted - PageURLRecords are the owning containers.  This is a tricky way to track how many page urls are retaining 
+          an IconRecord and therefore tracking when we should try to get rid of one.
+        (WebCore::IconRecord::~IconRecord):
+        (WebCore::IconRecord::image):
+        (WebCore::IconRecord::setImageData):
+        (WebCore::IconRecord::loadImageFromResource):
+        (WebCore::IconRecord::imageDataStatus): Return whether the image data hasn't been read yet, exists in memory, or is absent (site with no icon)
+        (WebCore::IconRecord::snapshot): Returns a snapshot of the icon's data - url, timestamp, and image data - to be written to disk
+        * loader/icon/IconRecord.h: Added.
+        (WebCore::IconSnapshot::IconSnapshot):
+        (WebCore::IconRecord::getTimestamp):
+        (WebCore::IconRecord::setTimestamp):
+        (WebCore::IconRecord::iconURL):
+        (WebCore::IconRecord::retainingPageURLs):
+
+        * loader/icon/PageURLRecord.cpp: Added.
+        (WebCore::PageURLRecord::PageURLRecord): PageURLRecord is fundamentally a pairing of a Page URL to an Icon.  It has manual ref counting for the sake
+          of "retainIconForPageURL" and "releaseIconForPageURL", and can provide a quick snapshot of it's Page URL -> Icon URL mapping for writing to
+          the database
+        (WebCore::PageURLRecord::setIconRecord):
+        (WebCore::PageURLRecord::snapshot):
+        * loader/icon/PageURLRecord.h: Added.
+        (WebCore::PageURLSnapshot::PageURLSnapshot):
+        (WebCore::PageURLRecord::url):
+        (WebCore::PageURLRecord::PageURLRecord::iconRecord):
+        (WebCore::PageURLRecord::retain):
+        (WebCore::PageURLRecord::release):
+        (WebCore::PageURLRecord::retainCount):
+
+        * platform/SharedBuffer.cpp:
+        (WebCore::SharedBuffer::copy): Added a deep copy method for the purposes of handing icon data across the thread boundary into the icon database
+        * platform/SharedBuffer.h:
+
+        * platform/graphics/svg/SVGImageEmptyClients.h:
+        (WebCore::SVGEmptyFrameLoaderClient::registerForIconNotification):
+
+        * platform/win/TemporaryLinkStubs.cpp:
+        (WebCore::callOnMainThread): Only other IconDatabase utilizing platform - keep their build going
+
 2007-09-07  David Kilzer  <ddkilzer@apple.com>
 
         Reviewed by Timothy Hatcher.
index a077c43c3a77f1f037a5bc23c05ef0f6d9858870..823e21f7b5732fc501e08e085a4bfc0c1293a879 100644 (file)
@@ -211,24 +211,27 @@ __ZN7WebCore12EventHandler8keyEventEP7NSEvent
 __ZN7WebCore12EventHandler9mouseDownEP7NSEvent
 __ZN7WebCore12IconDatabase10setEnabledEb
 __ZN7WebCore12IconDatabase11defaultIconERKNS_7IntSizeE
-__ZN7WebCore12IconDatabase11setImportedEb
 __ZN7WebCore12IconDatabase14iconForPageURLERKNS_6StringERKNS_7IntSizeEb
 __ZN7WebCore12IconDatabase14removeAllIconsEv
+__ZN7WebCore12IconDatabase15iconRecordCountEv
 __ZN7WebCore12IconDatabase17iconURLForPageURLERKNS_6StringE
-__ZN7WebCore12IconDatabase18hasEntryForIconURLERKNS_6StringE
+__ZN7WebCore12IconDatabase19pageURLMappingCountEv
+__ZN7WebCore12IconDatabase20allowDatabaseCleanupEv
+__ZN7WebCore12IconDatabase20delayDatabaseCleanupEv
 __ZN7WebCore12IconDatabase20retainIconForPageURLERKNS_6StringE
+__ZN7WebCore12IconDatabase20retainedPageURLCountEv
 __ZN7WebCore12IconDatabase20setIconURLForPageURLERKNS_6StringES3_
 __ZN7WebCore12IconDatabase21releaseIconForPageURLERKNS_6StringE
 __ZN7WebCore12IconDatabase21setIconDataForIconURLEN3WTF10PassRefPtrINS_12SharedBufferEEERKNS_6StringE
 __ZN7WebCore12IconDatabase23defaultDatabaseFilenameEv
-__ZN7WebCore12IconDatabase23isIconExpiredForIconURLERKNS_6StringE
-__ZN7WebCore12IconDatabase23setHaveNoIconForIconURLERKNS_6StringE
+__ZN7WebCore12IconDatabase23iconRecordCountWithDataEv
+__ZN7WebCore12IconDatabase23importIconURLForPageURLERKNS_6StringES3_
+__ZN7WebCore12IconDatabase24importIconDataForIconURLEN3WTF10PassRefPtrINS_12SharedBufferEEERKNS_6StringE
 __ZN7WebCore12IconDatabase25setPrivateBrowsingEnabledEb
 __ZN7WebCore12IconDatabase27checkIntegrityBeforeOpeningEv
 __ZN7WebCore12IconDatabase4openERKNS_6StringE
 __ZN7WebCore12IconDatabase5closeEv
-__ZN7WebCore12IconDatabase7isEmptyEv
-__ZN7WebCore12IconDatabase8importedEv
+__ZN7WebCore12IconDatabase9setClientEPNS_18IconDatabaseClientE
 __ZN7WebCore12SharedBuffer10wrapNSDataEP6NSData
 __ZN7WebCore12SharedBuffer12createNSDataEv
 __ZN7WebCore12TextEncodingC1ERKNS_6StringE
@@ -352,11 +355,10 @@ __ZN7WebCore4Page12setGroupNameERKNS_6StringE
 __ZN7WebCore4Page15backForwardListEv
 __ZN7WebCore4Page16setDefersLoadingEb
 __ZN7WebCore4Page23clearUndoRedoOperationsEv
+__ZN7WebCore4Page37setInLowQualityImageInterpolationModeEb
 __ZN7WebCore4Page6goBackEv
 __ZN7WebCore4Page8goToItemEPNS_11HistoryItemENS_13FrameLoadTypeE
 __ZN7WebCore4Page9goForwardEv
-__ZN7WebCore4Page37setInLowQualityImageInterpolationModeEb
-__ZNK7WebCore4Page34inLowQualityImageInterpolationModeEv
 __ZN7WebCore4PageC1EPNS_12ChromeClientEPNS_17ContextMenuClientEPNS_12EditorClientEPNS_10DragClientEPNS_15InspectorClientE
 __ZN7WebCore4PageD1Ev
 __ZN7WebCore5Cache11setDisabledEb
@@ -551,11 +553,8 @@ __ZNK7WebCore12AtomicString16deprecatedStringEv
 __ZNK7WebCore12EventHandler17eventMayStartDragERKNS_18PlatformMouseEventE
 __ZNK7WebCore12EventHandler20currentKeyboardEventEv
 __ZNK7WebCore12IconDatabase12databasePathEv
-__ZNK7WebCore12IconDatabase15iconRecordCountEv
-__ZNK7WebCore12IconDatabase19pageURLMappingCountEv
-__ZNK7WebCore12IconDatabase20retainedPageURLCountEv
-__ZNK7WebCore12IconDatabase23iconRecordCountWithDataEv
-__ZNK7WebCore12IconDatabase7enabledEv
+__ZNK7WebCore12IconDatabase24shouldStopThreadActivityEv
+__ZNK7WebCore12IconDatabase9isEnabledEv
 __ZNK7WebCore12RenderObject25backslashAsCurrencySymbolEv
 __ZNK7WebCore12SharedBuffer4dataEv
 __ZNK7WebCore12SharedBuffer4sizeEv
@@ -618,6 +617,7 @@ __ZNK7WebCore4KURL17lastPathComponentEv
 __ZNK7WebCore4KURL4hostEv
 __ZNK7WebCore4KURL4pathEv
 __ZNK7WebCore4KURL8getNSURLEv
+__ZNK7WebCore4Page34inLowQualityImageInterpolationModeEv
 __ZNK7WebCore5Frame10isFrameSetEv
 __ZNK7WebCore5Frame12eventHandlerEv
 __ZNK7WebCore5Frame12ownerElementEv
index 70d75a3a8067bdedf5dcfca0e3e317f2a7ead495..8b57c20d3a911cf8f8605b67ce8e1bcde1c2a9ee 100644 (file)
@@ -58,7 +58,6 @@
                                DebugInformationFormat="4"
                                DisableSpecificWarnings="4099;4100;4127;4138;4189;4244;4510;4512;4610;4706;4800;4996;4355;4291;4068;6011;6031;6211;6246;6255;6387"
                                ForcedIncludeFiles="WebCorePrefix.h"
-                               EnablePREfast="$(EnablePREfast)"
                        />
                        <Tool
                                Name="VCManagedResourceCompilerTool"
                                DebugInformationFormat="3"
                                DisableSpecificWarnings="4099;4100;4127;4138;4189;4244;4510;4512;4610;4706;4800;4996;4355;4291;4068;6011;6031;6211;6246;6255;6387"
                                ForcedIncludeFiles="WebCorePrefix.h"
-                               EnablePREfast="$(EnablePREfast)"
                        />
                        <Tool
                                Name="VCManagedResourceCompilerTool"
                                DebugInformationFormat="4"
                                DisableSpecificWarnings="4099;4100;4127;4138;4189;4244;4510;4512;4610;4706;4800;4996;4355;4291;4068;6011;6031;6211;6246;6255;6387"
                                ForcedIncludeFiles="WebCorePrefix.h"
-                               EnablePREfast="$(EnablePREfast)"
                        />
                        <Tool
                                Name="VCManagedResourceCompilerTool"
                                RelativePath="..\loader\FrameLoaderTypes.h"
                                >
                        </File>
-            <File
+                       <File
                                RelativePath="..\loader\FTPDirectoryDocument.cpp"
                                >
-            </File>
-            <File
+                       </File>
+                       <File
                                RelativePath="..\loader\FTPDirectoryDocument.h"
                                >
-            </File>
-            <File
+                       </File>
+                       <File
                                RelativePath="..\loader\FTPDirectoryParser.cpp"
                                >
-            </File>
-            <File
+                       </File>
+                       <File
                                RelativePath="..\loader\FTPDirectoryParser.h"
                                >
-            </File>
+                       </File>
                        <File
                                RelativePath="..\loader\ImageDocument.cpp"
                                >
                                        >
                                </File>
                                <File
-                                       RelativePath="..\loader\icon\IconDataCache.cpp"
+                                       RelativePath="..\loader\icon\IconDatabaseClient.h"
                                        >
                                </File>
                                <File
-                                       RelativePath="..\loader\icon\IconDataCache.h"
+                                       RelativePath="..\loader\icon\IconLoader.cpp"
                                        >
                                </File>
                                <File
-                                       RelativePath="..\loader\icon\IconLoader.cpp"
+                                       RelativePath="..\loader\icon\IconLoader.h"
                                        >
                                </File>
                                <File
-                                       RelativePath="..\loader\icon\IconLoader.h"
+                                       RelativePath="..\loader\icon\IconRecord.cpp"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\loader\icon\IconRecord.h"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\loader\icon\PageURLRecord.cpp"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\loader\icon\PageURLRecord.h"
                                        >
                                </File>
                                <File
                                RelativePath="..\platform\AtomicStringImpl.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\platform\AutodrainedPool.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\platform\Base64.cpp"
                                >
                                RelativePath="..\platform\TextStyle.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\platform\Threading.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\platform\Timer.cpp"
                                >
                                        RelativePath="..\platform\win\KeyEventWin.cpp"
                                        >
                                </File>
-        <File
+                               <File
                                        RelativePath="..\platform\win\Language.cpp"
                                        >
-        </File>
-        <File
+                               </File>
+                               <File
                                        RelativePath="..\platform\win\MIMETypeRegistryWin.cpp"
                                        >
                                </File>
                                        RelativePath="..\platform\win\SearchPopupMenuWin.cpp"
                                        >
                                </File>
-                <File
+                               <File
                                        RelativePath="..\platform\win\SharedBufferWin.cpp"
                                        >
-                </File>
-                <File
+                               </File>
+                               <File
                                        RelativePath="..\platform\win\SharedTimerWin.cpp"
                                        >
                                </File>
index 73e9ad394bae76cc03f1d61d90224b40bd2a4042..ecdeb65ffc0b8f846e5170ac705edb10cb082a8c 100644 (file)
                51741D100B07259A00ED442C /* BackForwardList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51741D0C0B07259A00ED442C /* BackForwardList.cpp */; };
                51741D110B07259A00ED442C /* HistoryItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 51741D0D0B07259A00ED442C /* HistoryItem.h */; settings = {ATTRIBUTES = (Private, ); }; };
                51741D120B07259A00ED442C /* HistoryItem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51741D0E0B07259A00ED442C /* HistoryItem.cpp */; };
-               5186C0560A9C21470034FE94 /* IconDataCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5186C0550A9C21470034FE94 /* IconDataCache.cpp */; };
                51AA3F6F0BD5AA9E00892971 /* ResourceLoaderMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51AA3F6E0BD5AA9E00892971 /* ResourceLoaderMac.mm */; };
                51C81B890C4422F70019ECE3 /* FTPDirectoryParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51C81B870C4422F70019ECE3 /* FTPDirectoryParser.cpp */; };
                51C81B8A0C4422F70019ECE3 /* FTPDirectoryParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 51C81B880C4422F70019ECE3 /* FTPDirectoryParser.h */; };
                51E1ECB00C91C54600DC255B /* Threading.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51E1ECAE0C91C54600DC255B /* Threading.mm */; };
                51E1ECB30C91C55600DC255B /* AutodrainedPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E1ECB10C91C55600DC255B /* AutodrainedPool.h */; };
                51E1ECB40C91C55600DC255B /* Threading.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E1ECB20C91C55600DC255B /* Threading.h */; };
+               51E1ECBE0C91C90400DC255B /* IconDatabaseClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E1ECB80C91C90400DC255B /* IconDatabaseClient.h */; };
+               51E1ECC00C91C90400DC255B /* IconRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51E1ECBA0C91C90400DC255B /* IconRecord.cpp */; };
+               51E1ECC10C91C90400DC255B /* IconRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E1ECBB0C91C90400DC255B /* IconRecord.h */; };
+               51E1ECC20C91C90400DC255B /* PageURLRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51E1ECBC0C91C90400DC255B /* PageURLRecord.cpp */; };
+               51E1ECC30C91C90400DC255B /* PageURLRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E1ECBD0C91C90400DC255B /* PageURLRecord.h */; };
                51E4ADB60C42B4CF0042BC55 /* FTPDirectoryDocument.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51E4ADB20C42B4CF0042BC55 /* FTPDirectoryDocument.cpp */; };
                51E4ADB70C42B4CF0042BC55 /* FTPDirectoryDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E4ADB30C42B4CF0042BC55 /* FTPDirectoryDocument.h */; };
                51F11E150A48C2920034A24E /* SQLTransaction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51F11E140A48C2920034A24E /* SQLTransaction.cpp */; };
                656D37440ADBA5DE00A4554D /* NetscapePlugInStreamLoaderMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 656D372C0ADBA5DE00A4554D /* NetscapePlugInStreamLoaderMac.mm */; };
                656D37450ADBA5DE00A4554D /* WebPlugInStreamLoaderDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 656D372D0ADBA5DE00A4554D /* WebPlugInStreamLoaderDelegate.h */; settings = {ATTRIBUTES = (Private, ); }; };
                656D37480ADBA5DE00A4554D /* SubresourceLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 656D37300ADBA5DE00A4554D /* SubresourceLoader.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               657429170A9C2D0B00C52C97 /* IconDataCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 657429140A9C2D0B00C52C97 /* IconDataCache.h */; };
                657429180A9C2D0B00C52C97 /* SQLStatement.h in Headers */ = {isa = PBXBuildFile; fileRef = 657429150A9C2D0B00C52C97 /* SQLStatement.h */; };
                657429190A9C2D0B00C52C97 /* SQLTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 657429160A9C2D0B00C52C97 /* SQLTransaction.h */; };
                657BD74D09AFDC54005A2056 /* TextCodec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 657BD74909AFDC54005A2056 /* TextCodec.cpp */; };
                51741D0C0B07259A00ED442C /* BackForwardList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BackForwardList.cpp; sourceTree = "<group>"; };
                51741D0D0B07259A00ED442C /* HistoryItem.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = HistoryItem.h; sourceTree = "<group>"; };
                51741D0E0B07259A00ED442C /* HistoryItem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = HistoryItem.cpp; sourceTree = "<group>"; };
-               5186C0550A9C21470034FE94 /* IconDataCache.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = IconDataCache.cpp; sourceTree = "<group>"; };
                51AA3F6E0BD5AA9E00892971 /* ResourceLoaderMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResourceLoaderMac.mm; sourceTree = "<group>"; };
                51C81B870C4422F70019ECE3 /* FTPDirectoryParser.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = FTPDirectoryParser.cpp; sourceTree = "<group>"; };
                51C81B880C4422F70019ECE3 /* FTPDirectoryParser.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = FTPDirectoryParser.h; sourceTree = "<group>"; };
                51E1ECAE0C91C54600DC255B /* Threading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Threading.mm; sourceTree = "<group>"; };
                51E1ECB10C91C55600DC255B /* AutodrainedPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutodrainedPool.h; sourceTree = "<group>"; };
                51E1ECB20C91C55600DC255B /* Threading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Threading.h; sourceTree = "<group>"; };
+               51E1ECB80C91C90400DC255B /* IconDatabaseClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IconDatabaseClient.h; sourceTree = "<group>"; };
+               51E1ECBA0C91C90400DC255B /* IconRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IconRecord.cpp; sourceTree = "<group>"; };
+               51E1ECBB0C91C90400DC255B /* IconRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IconRecord.h; sourceTree = "<group>"; };
+               51E1ECBC0C91C90400DC255B /* PageURLRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PageURLRecord.cpp; sourceTree = "<group>"; };
+               51E1ECBD0C91C90400DC255B /* PageURLRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PageURLRecord.h; sourceTree = "<group>"; };
                51E4ADB20C42B4CF0042BC55 /* FTPDirectoryDocument.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = FTPDirectoryDocument.cpp; sourceTree = "<group>"; };
                51E4ADB30C42B4CF0042BC55 /* FTPDirectoryDocument.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = FTPDirectoryDocument.h; sourceTree = "<group>"; };
                51F11E140A48C2920034A24E /* SQLTransaction.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SQLTransaction.cpp; sourceTree = "<group>"; };
                656D372C0ADBA5DE00A4554D /* NetscapePlugInStreamLoaderMac.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 30; path = NetscapePlugInStreamLoaderMac.mm; sourceTree = "<group>"; };
                656D372D0ADBA5DE00A4554D /* WebPlugInStreamLoaderDelegate.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = WebPlugInStreamLoaderDelegate.h; sourceTree = "<group>"; };
                656D37300ADBA5DE00A4554D /* SubresourceLoader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SubresourceLoader.h; sourceTree = "<group>"; };
-               657429140A9C2D0B00C52C97 /* IconDataCache.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = IconDataCache.h; sourceTree = "<group>"; };
                657429150A9C2D0B00C52C97 /* SQLStatement.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SQLStatement.h; sourceTree = "<group>"; };
                657429160A9C2D0B00C52C97 /* SQLTransaction.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SQLTransaction.h; sourceTree = "<group>"; };
                657BD74909AFDC54005A2056 /* TextCodec.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = TextCodec.cpp; sourceTree = "<group>"; };
                        children = (
                                5126E6B90A2E3B12005C29FA /* IconDatabase.cpp */,
                                5126E6BA0A2E3B12005C29FA /* IconDatabase.h */,
-                               5186C0550A9C21470034FE94 /* IconDataCache.cpp */,
-                               657429140A9C2D0B00C52C97 /* IconDataCache.h */,
+                               51E1ECB80C91C90400DC255B /* IconDatabaseClient.h */,
                                513F14510AB634C400094DDF /* IconLoader.cpp */,
                                513F14520AB634C400094DDF /* IconLoader.h */,
+                               51E1ECBA0C91C90400DC255B /* IconRecord.cpp */,
+                               51E1ECBB0C91C90400DC255B /* IconRecord.h */,
+                               51E1ECBC0C91C90400DC255B /* PageURLRecord.cpp */,
+                               51E1ECBD0C91C90400DC255B /* PageURLRecord.h */,
                                51D3EA130A3D24D300BADA35 /* SQLDatabase.cpp */,
                                51D3EA140A3D24D300BADA35 /* SQLDatabase.h */,
                                51D3EA150A3D24D300BADA35 /* SQLStatement.cpp */,
                                85ACABB00A9CAF8000671E90 /* DOMDocument.h in Headers */,
                                8518DCE90A9CC80D0091B7A6 /* DOMDOMImplementation.h in Headers */,
                                8518DD780A9CF31B0091B7A6 /* DOMNamedNodeMap.h in Headers */,
-                               657429170A9C2D0B00C52C97 /* IconDataCache.h in Headers */,
                                657429180A9C2D0B00C52C97 /* SQLStatement.h in Headers */,
                                657429190A9C2D0B00C52C97 /* SQLTransaction.h in Headers */,
                                AAC8DAB20AA1002000DC0907 /* SVGMetadataElement.h in Headers */,
                                EDE3A5000C7A430600956A37 /* ColorMac.h in Headers */,
                                51E1ECB30C91C55600DC255B /* AutodrainedPool.h in Headers */,
                                51E1ECB40C91C55600DC255B /* Threading.h in Headers */,
+                               51E1ECBE0C91C90400DC255B /* IconDatabaseClient.h in Headers */,
+                               51E1ECC10C91C90400DC255B /* IconRecord.h in Headers */,
+                               51E1ECC30C91C90400DC255B /* PageURLRecord.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                0867D690FE84028FC02AAC07 /* Project object */ = {
                        isa = PBXProject;
                        buildConfigurationList = 149C284308902B11008A9EFC /* Build configuration list for PBXProject "WebCore" */;
+                       compatibilityVersion = "Xcode 2.4";
                        hasScannedForEncodings = 1;
                        knownRegions = (
                                English,
                                85ACABB10A9CAF8000671E90 /* DOMDocument.mm in Sources */,
                                8518DCEA0A9CC80D0091B7A6 /* DOMDOMImplementation.mm in Sources */,
                                8518DD790A9CF31B0091B7A6 /* DOMNamedNodeMap.mm in Sources */,
-                               5186C0560A9C21470034FE94 /* IconDataCache.cpp in Sources */,
                                AAC8DAB10AA1002000DC0907 /* SVGMetadataElement.cpp in Sources */,
                                85DF2C5D0AA341F600AD64C5 /* DOMHTMLFormElement.mm in Sources */,
                                85DF2EEE0AA387CB00AD64C5 /* DOMHTMLElement.mm in Sources */,
                                514B3F760C722055000530DF /* FileSystemMac.mm in Sources */,
                                51E1ECAF0C91C54600DC255B /* AutodrainedPool.mm in Sources */,
                                51E1ECB00C91C54600DC255B /* Threading.mm in Sources */,
+                               51E1ECC00C91C90400DC255B /* IconRecord.cpp in Sources */,
+                               51E1ECC20C91C90400DC255B /* PageURLRecord.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index 40bcf7444a0540c90d41f61351fa6ff0f869a84f..204e4ba9710a8bd38b8ed4bd17a15d790255b63b 100644 (file)
@@ -711,4 +711,10 @@ void DocumentLoader::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loa
         frame->loader()->checkLoadComplete();    
 }
 
+void DocumentLoader::iconLoadDecisionAvailable()
+{
+    if (m_frame)
+        m_frame->loader()->iconLoadDecisionAvailable();
+}
+
 }
index b9ddaecd7dacd783ed4a7e88f9c20383e4cade8b..6ef9b0faa87cf194dd3888124ab1654fbb9b75e0 100644 (file)
@@ -29,6 +29,7 @@
 #ifndef DocumentLoader_h
 #define DocumentLoader_h
 
+#include "IconDatabase.h"
 #include "NavigationAction.h"
 #include "Shared.h"
 #include "PlatformString.h"
@@ -136,7 +137,9 @@ namespace WebCore {
 
         bool startLoadingMainResource(unsigned long identifier);
         void cancelMainResourceLoad(const ResourceError&);
-
+        
+        void iconLoadDecisionAvailable();
+        
         bool isLoadingMainResource() const;
         bool isLoadingSubresources() const;
         bool isLoadingPlugIns() const;
index bf44814023f9712dc6f8870da5422f636b33a8f3..06e740654dcf2b0a92b354692dd528dbbfef8f1a 100644 (file)
@@ -1029,6 +1029,15 @@ void FrameLoader::endIfNotLoadingMainResource()
         startIconLoader();
 }
 
+void FrameLoader::iconLoadDecisionAvailable()
+{
+    if (!m_mayLoadIconLater)
+        return;
+    LOG(IconDatabase, "FrameLoader %p was told a load decision is available for its icon", this);
+    startIconLoader();
+    m_mayLoadIconLater = false;
+}
+
 void FrameLoader::startIconLoader()
 {
     // FIXME: We kick off the icon loader when the frame is done receiving its main resource.
@@ -1036,23 +1045,53 @@ void FrameLoader::startIconLoader()
     if (!isLoadingMainFrame())
         return;
 
-    if (!iconDatabase() || !iconDatabase()->enabled())
+    if (!iconDatabase() || !iconDatabase()->isEnabled())
         return;
-
+    
     KURL url(iconURL());
     String urlString(url.url());
     if (urlString.isEmpty())
         return;
 
-    // If we already have an unexpired icon, we won't kick off a load but we *will* map the appropriate URLs to it
-    if (iconDatabase()->hasEntryForIconURL(urlString) && loadType() != FrameLoadTypeReload && !iconDatabase()->isIconExpiredForIconURL(urlString)) {
-        LOG(IconDatabase, "FrameLoader::startIconLoader() - Committing iconURL %s to database", urlString.ascii().data());
-        commitIconURLToIconDatabase(url);
-        return;
+    // If we're not reloading and the icon database doesn't say to load now then bail before we actually start the load
+    if (loadType() != FrameLoadTypeReload) {
+        IconLoadDecision decision = iconDatabase()->loadDecisionForIconURL(urlString, m_documentLoader.get());
+        if (decision == IconLoadNo) {
+            LOG(IconDatabase, "FrameLoader::startIconLoader() - Told not to load this icon, committing iconURL %s to database for pageURL mapping", urlString.ascii().data());
+            commitIconURLToIconDatabase(url);
+            
+            // We were told not to load this icon - that means this icon is already known by the database
+            // If the icon data hasn't been read in from disk yet, kick off the read of the icon from the database to make sure someone
+            // has done it.  This is after registering for the notification so the WebView can call the appropriate delegate method.
+            // Otherwise if the icon data *is* available, and this icon load was previously deferred, we need to notifiy the delegate
+            if (!iconDatabase()->iconDataKnownForIconURL(urlString)) {
+                LOG(IconDatabase, "Told not to load icon %s but icon data is not yet available - registering for notification and requesting load from disk", urlString.ascii().data());
+                m_client->registerForIconNotification();
+                iconDatabase()->iconForPageURL(m_URL.url(), IntSize(0, 0));
+                iconDatabase()->iconForPageURL(originalRequestURL().url(), IntSize(0, 0));
+            } else if (m_mayLoadIconLater)
+                m_client->dispatchDidReceiveIcon();
+                
+            return;
+        } 
+        
+        if (decision == IconLoadUnknown) {
+            // In this case, we may end up loading the icon later, but we still want to commit the icon url mapping to the database
+            // just in case we don't end up loading later - if we commit the mapping a second time after the load, that's no big deal
+            // We also tell the client to register for the notification that the icon is received now so it isn't missed in case the 
+            // icon is later read in from disk
+            LOG(IconDatabase, "FrameLoader %p might load icon %s later", this, urlString.ascii().data());
+            m_mayLoadIconLater = true;    
+            m_client->registerForIconNotification();
+            commitIconURLToIconDatabase(url);
+            return;
+        }
     }
 
+    // This is either a reload or the icon database said "yes, load the icon", so kick off the load!
     if (!m_iconLoader)
         m_iconLoader.set(IconLoader::create(m_frame).release());
+        
     m_iconLoader->startLoading();
 }
 
index fff7382eff41f70473ee0149c3a01094ba46c85a..37b2b1fa2ef03f1f3cf3794f0d4b904e57e4ccf7 100644 (file)
@@ -437,6 +437,7 @@ namespace WebCore {
 
         bool committingFirstRealLoad() const { return !m_creatingInitialEmptyDocument && !m_committedFirstRealDocumentLoad; }
 
+        void iconLoadDecisionAvailable();
     private:
         PassRefPtr<HistoryItem> createHistoryItem(bool useOriginal);
         PassRefPtr<HistoryItem> createHistoryItemTree(Frame* targetFrame, bool clipAtTarget);
@@ -608,6 +609,7 @@ namespace WebCore {
         KURL m_workingURL;
 
         OwnPtr<IconLoader> m_iconLoader;
+        bool m_mayLoadIconLater;
 
         bool m_cancellingWithLoadInProgress;
 
index fe552804c2a3005269c19e8f52e36f8f75250e83..4b7c8c3e7fb1dbcc9874f82943f2f3044d6be7a9 100644 (file)
@@ -209,6 +209,8 @@ namespace WebCore {
         virtual void windowObjectCleared() const = 0;
         virtual void didPerformFirstNavigation() const = 0; // "Navigation" here means a transition from one page to another that ends up in the back/forward list.
         
+        virtual void registerForIconNotification(bool listen = true) = 0;
+        
 #if PLATFORM(MAC)
         virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long identifier, NSCachedURLResponse*) const = 0;
 #endif
diff --git a/WebCore/loader/icon/IconDataCache.cpp b/WebCore/loader/icon/IconDataCache.cpp
deleted file mode 100644 (file)
index a410311..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
- */
-
-#include "config.h"
-#include "IconDataCache.h"
-
-#include "BitmapImage.h"
-#include <limits.h>
-#include "Logging.h"
-#include "SQLStatement.h"
-
-
-namespace WebCore {
-
-IconDataCache::IconDataCache(const String& url)
-    : m_iconURL(url)
-    , m_stamp(0)
-    , m_image(0)
-    , m_dataSet(false)
-{
-
-}
-
-IconDataCache::~IconDataCache()
-{
-    // Upon destruction of a IconDataCache, its image should no longer be in use anywhere
-    delete m_image;
-}
-
-Image* IconDataCache::getImage(const IntSize& size)
-{
-    // FIXME rdar://4680377 - For size right now, we are returning our one and only image and the Bridge
-    // is resizing it in place.  We need to actually store all the original representations here and return a native
-    // one, or resize the best one to the requested size and cache that result.
-    if (m_image)
-        return m_image;
-    
-    return 0;
-}
-
-void IconDataCache::setImageData(PassRefPtr<SharedBuffer> data)
-{
-    // It's okay to delete the raw image data here. Any existing clients using this icon will be
-    // managing an image that was created with a copy of this raw image data.
-    if (m_image)
-        delete m_image;
-    m_image = new BitmapImage();
-
-    // Copy the provided data into the buffer of the new Image object.
-    if (!m_image->setData(data, true)) {
-        LOG(IconDatabase, "Manual image data for iconURL '%s' FAILED - it was probably invalid image data", m_iconURL.ascii().data());
-        delete m_image;
-        m_image = 0;
-    }
-    
-    m_dataSet = true;
-}
-
-void IconDataCache::loadImageFromResource(const char* resource)
-{
-    if (!resource)
-        return;
-    delete m_image;
-    m_image = Image::loadPlatformResource(resource);
-    m_dataSet = true;
-}
-
-void IconDataCache::writeToDatabase(SQLDatabase& db)
-{
-    if (m_iconURL.isEmpty())
-        return;
-    
-    // First we'll try an update in case we're already in the DB
-    SQLStatement updateAttempt(db, "UPDATE Icon SET stamp = ?, data = ? WHERE url = ?;");
-    if (updateAttempt.prepare() != SQLResultOk) {
-        LOG_ERROR("Failed to prepare SQL statement to update icon data for url %s", m_iconURL.ascii().data());
-        return;
-    }
-    
-    // Bind the url and timestamp
-    if (updateAttempt.bindInt64(1, m_stamp) != SQLResultOk) {
-        LOG_ERROR("Failed to bind iconID to SQL statement to update icon data for url %s", m_iconURL.ascii().data());
-        return;    
-    }
-    if (updateAttempt.bindText16(3, m_iconURL) != SQLResultOk) {
-        LOG_ERROR("Failed to bind url to SQL statement to update icon data for url %s", m_iconURL.ascii().data());
-        return;    
-    }
-    
-    // If we *have* image data, bind it to this statement - Otherwise the DB will get "null" for the blob data, 
-    // signifying that this icon doesn't have any data    
-    if (m_image && m_image->data() && !m_image->data()->isEmpty())
-        if (updateAttempt.bindBlob(2, m_image->data()->data(), m_image->data()->size()) != SQLResultOk) {
-            LOG_ERROR("Failed to bind icon data to SQL statement to update icon data for url %s", m_iconURL.ascii().data());
-            return;
-        }
-    
-    if (updateAttempt.step() != SQLResultDone) {
-        LOG_ERROR("Failed to update icon data for IconURL %s", m_iconURL.ascii().data());
-        return;
-    }
-    
-    // If the UPDATE command actually altered a row, we're done
-    if (db.lastChanges())
-        return;
-        
-        
-    // Otherwise, we've never inserted this Icon URL before, so we'll do so now
-    updateAttempt.finalize();
-    SQLStatement insertStatement(db, "INSERT INTO Icon (url,stamp,data) VALUES (?, ?, ?);");
-    insertStatement.prepare();
-        
-    // Bind the url and timestamp
-    insertStatement.bindText16(1, m_iconURL);
-    insertStatement.bindInt64(2, m_stamp);
-        
-    // Then, if we *have* data, we bind it.  Otherwise the DB will get "null" for the blob data, 
-    // signifying that this icon doesn't have any data
-    if (m_image && m_image->data() && !m_image->data()->isEmpty())
-        insertStatement.bindBlob(3, m_image->data()->data(), m_image->data()->size());
-    
-    // Finally we step and make sure the step was successful
-    if (insertStatement.step() != SQLResultDone)
-        LOG_ERROR("Unable to set icon data for IconURL %s", m_iconURL.ascii().data());
-}
-
-ImageDataStatus IconDataCache::imageDataStatus()
-{
-    if (!m_dataSet)
-        return ImageDataStatusUnknown;
-    if (!m_image)
-        return ImageDataStatusMissing;
-    return ImageDataStatusPresent;
-}
-
-} // namespace WebCore    
diff --git a/WebCore/loader/icon/IconDataCache.h b/WebCore/loader/icon/IconDataCache.h
deleted file mode 100644 (file)
index f5801f9..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
- */
-#ifndef IconDataCache_h
-#define IconDataCache_h
-
-#include "PlatformString.h"
-
-namespace WebCore { 
-
-class Image;
-class IntSize;
-class SharedBuffer;
-class SQLDatabase;
-
-enum ImageDataStatus {
-    ImageDataStatusPresent, ImageDataStatusMissing, ImageDataStatusUnknown
-};
-    
-class IconDataCache {
-public:
-    IconDataCache(const String& url); 
-    ~IconDataCache();
-    
-    time_t getTimestamp() { return m_stamp; }
-    void setTimestamp(time_t stamp) { m_stamp = stamp; }
-        
-    Image* getImage(const IntSize&);    
-    String getIconURL() { return m_iconURL; }
-
-    void setImageData(PassRefPtr<SharedBuffer> data);
-    
-    void loadImageFromResource(const char*);
-    
-    void writeToDatabase(SQLDatabase& db);
-    
-    ImageDataStatus imageDataStatus();
-    
-private:
-    String m_iconURL;
-    time_t m_stamp;
-    Image* m_image;
-    
-    // This allows us to cache whether or not a SiteIcon has had its data set yet
-    // This helps the IconDatabase know if it has to set the data on a new object or not,
-    // and also to determine if the icon is missing data or if it just hasn't been brought
-    // in from the DB yet
-    bool m_dataSet;
-    
-    // FIXME - Right now WebCore::Image doesn't have a very good API for accessing multiple representations
-    // Even the NSImage way of doing things that we do in WebKit isn't very clean...  once we come up with a 
-    // better way of handling that, we'll likely have a map of size-to-images similar to below
-    // typedef HashMap<IntSize, Image*> SizeImageMap;
-    // SizeImageMap m_images;
-};
-
-
-} //namespace WebCore
-
-#endif
index 485c3065eaf5fe9cd0b46d6aa9ef75b7d0590c41..c7e58642491e53d1d0fbb463963745b0cbef3aab 100644 (file)
 #include "config.h"
 #include "IconDatabase.h"
 
+#include "AutodrainedPool.h"
 #include "CString.h"
+#include "DocumentLoader.h"
 #include "FileSystem.h"
-#include "IconDataCache.h"
+#include "IconDatabaseClient.h"
+#include "IconRecord.h"
 #include "Image.h"
+#include "IntSize.h"
+#include "KURL.h"
 #include "Logging.h"
+#include "PageURLRecord.h"
 #include "SQLStatement.h"
 #include "SQLTransaction.h"
 #include "SystemTime.h"
 #include <sys/stat.h>
 #endif
 
+// For methods that are meant to support API from the main thread - should not be called internally
+#define ASSERT_NOT_SYNC_THREAD() ASSERT(!m_syncThreadRunning || !IS_ICON_SYNC_THREAD())
+
+// For methods that are meant to support the sync thread ONLY
+#define IS_ICON_SYNC_THREAD() pthread_equal(pthread_self(), m_syncThread)
+#define ASSERT_ICON_SYNC_THREAD() ASSERT(IS_ICON_SYNC_THREAD())
+
 namespace WebCore {
 
 static IconDatabase* sharedIconDatabase = 0;
+static int databaseCleanupCounter = 0;
 
 // This version number is in the DB and marks the current generation of the schema
-// Theoretically once the switch is flipped this should never change
-// Currently, an out-of-date schema causes the DB to be wiped and reset.  This isn't 
+// Currently, a mismatched schema causes the DB to be wiped and reset.  This isn't 
 // so bad during development but in the future, we would need to write a conversion
 // function to advance older released schemas to "current"
-const int currentDatabaseVersion = 5;
+const int currentDatabaseVersion = 6;
 
-// Icons expire once a day
-const int iconExpirationTime = 60*60*24; 
-// Absent icons are rechecked once a week
-const int missingIconExpirationTime = 60*60*24*7; 
+// Icons expire once every 4 days
+const int iconExpirationTime = 60*60*24*4; 
 
 const int updateTimerDelay = 5; 
 
 static bool checkIntegrityOnOpen = false;
 
-const String& IconDatabase::defaultDatabaseFilename()
+#ifndef NDEBUG
+static String urlForLogging(const String& url)
 {
-    static String defaultDatabaseFilename = "Icons.db";
-    return defaultDatabaseFilename;
+    static unsigned urlTruncationLength = 120;
+
+    if (url.length() < urlTruncationLength)
+        return url;
+    return url.substring(0, urlTruncationLength) + "...";
 }
+#endif
 
-void IconDatabase::checkIntegrityBeforeOpening()
+static IconDatabaseClient* defaultClient() 
 {
-    checkIntegrityOnOpen = true;
+    static IconDatabaseClient* defaultClient = new IconDatabaseClient();
+    return defaultClient;
 }
 
 IconDatabase* iconDatabase()
@@ -81,28 +98,21 @@ IconDatabase* iconDatabase()
     return sharedIconDatabase;
 }
 
-IconDatabase::IconDatabase()
-    : m_timeStampForIconURLStatement(0)
-    , m_iconURLForPageURLStatement(0)
-    , m_hasIconForIconURLStatement(0)
-    , m_forgetPageURLStatement(0)
-    , m_setIconIDForPageURLStatement(0)
-    , m_getIconIDForIconURLStatement(0)
-    , m_addIconForIconURLStatement(0)
-    , m_imageDataForIconURLStatement(0)
-    , m_importedStatement(0)
-    , m_setImportedStatement(0)
-    , m_currentDB(&m_mainDB)
-    , m_defaultIconDataCache(0)
-    , m_isEnabled(false)
-    , m_privateBrowsingEnabled(false)
-    , m_startupTimer(this, &IconDatabase::pruneUnretainedIconsOnStartup)
-    , m_updateTimer(this, &IconDatabase::updateDatabase)
-    , m_initialPruningComplete(false)
-    , m_imported(false)
-    , m_isImportedSet(false)
+// ************************
+// *** Main Thread Only ***
+// ************************
+
+void IconDatabase::setClient(IconDatabaseClient* client)
 {
-    
+    // We don't allow a null client, because we never null check it anywhere in this code
+    // Also don't allow a client change after the thread has already began 
+    // (setting the client should occur before the database is opened)
+    ASSERT(client);
+    ASSERT(!m_syncThreadRunning);
+    if (!client || m_syncThreadRunning)
+        return;
+        
+    m_client = client;
 }
 
 bool makeAllDirectories(const String& path)
@@ -112,12 +122,15 @@ bool makeAllDirectories(const String& path)
     if (!SHCreateDirectoryEx(0, fullPath.charactersWithNullTermination(), 0)) {
         DWORD error = GetLastError();
         if (error != ERROR_FILE_EXISTS && error != ERROR_ALREADY_EXISTS) {
-            LOG_ERROR("Failed to create path %s", path.ascii().data());
+            LOG_ERROR("Failed to create path %s", path.utf8().data());
             return false;
         }
     }
 #else
     CString fullPath = path.utf8();
+    if (!access(fullPath.data(), F_OK))
+        return true;
+        
     char* p = fullPath.mutableData() + 1;
     int length = fullPath.length();
     
@@ -140,15 +153,17 @@ bool makeAllDirectories(const String& path)
 
 bool IconDatabase::open(const String& databasePath)
 {
+    ASSERT_NOT_SYNC_THREAD();
+
     if (!m_isEnabled)
         return false;
-        
+
     if (isOpen()) {
         LOG_ERROR("Attempt to reopen the IconDatabase which is already open.  Must close it first.");
         return false;
     }
-    
-    // <rdar://problem/4730811> - Need to create the database path if it doesn't already exist
+    // Need to create the database path if it doesn't already exist
     makeAllDirectories(databasePath);
     
     // First we'll formulate the full path for the database file
@@ -165,738 +180,1402 @@ bool IconDatabase::open(const String& databasePath)
         dbFilename = databasePath + "/" + defaultDatabaseFilename();
 #endif
 
-    String journalFilename = dbFilename + "-journal";
-    if (!checkIntegrityOnOpen)
-        checkIntegrityOnOpen = fileExists(journalFilename);
-    
-    // <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.  And, if we can't, we'll return false.  
-    // WebKit will then ignore us and act as if the database is disabled
-    if (!m_mainDB.open(dbFilename)) {
-        LOG_ERROR("Unable to open icon database at path %s - %s", dbFilename.ascii().data(), m_mainDB.lastErrorMsg());
-        return false;
-    }
-    
-    if (checkIntegrityOnOpen) {
-        checkIntegrityOnOpen = false;
-        if (!checkIntegrity()) {
-            LOG(IconDatabase, "Integrity check was bad - dumping IconDatabase");
-            close();
-
-            // Should've been consumed by SQLite, delete just to make sure we don't see it again in the future;
-            deleteFile(journalFilename);
-            deleteFile(dbFilename);
-
-            // Reopen the main database, creating it from scratch
-            if (!m_mainDB.open(dbFilename)) {
-                LOG_ERROR("Unable to open icon database at path %s - %s", dbFilename.ascii().data(), m_mainDB.lastErrorMsg());
-                return false;
-            }
-        }
-    }
-        
-    if (!isValidDatabase(m_mainDB)) {
-        LOG(IconDatabase, "%s is missing or in an invalid state - reconstructing", dbFilename.ascii().data());
-        m_mainDB.clearAllTables();
-        createDatabaseTables(m_mainDB);
-    }
-    
-    // Reduce sqlite RAM cache size from default 2000 pages (~1.5kB per page). 3MB of cache for icon database is overkill
-    if (!SQLStatement(m_mainDB, "PRAGMA cache_size = 200;").executeCommand())         
-        LOG_ERROR("SQLite database could not set cache_size");
-    
-    // Open the in-memory table for private browsing
-    if (!m_privateBrowsingDB.open(":memory:"))
-        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
-    // will become "deferredTimer" or something along those lines, and will be set only when
-    // a deferred read/write is queued
-    if (isOpen())
-        m_startupTimer.startOneShot(0);
+    m_completeDatabasePath = dbFilename.copy();
     
-    return isOpen();
-}
+    // Lock here as well as first thing in the thread so the tread doesn't actually commence until the pthread_create() call 
+    // completes and m_syncThreadRunning is properly set
+    m_syncLock.lock();
+    m_syncThreadRunning = !pthread_create(&m_syncThread, NULL, IconDatabase::iconDatabaseSyncThreadStart, this);
+    m_syncLock.unlock();
 
-bool IconDatabase::isOpen() const
-{
-    return m_mainDB.isOpen() && m_privateBrowsingDB.isOpen();
+    return m_syncThreadRunning;
 }
 
 void IconDatabase::close()
 {
-    // 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();
-}
-
-String IconDatabase::databasePath() const
-{
-    return m_mainDB.isOpen() ? m_mainDB.path() : String();
-}
-    
-void IconDatabase::removeAllIcons()
-{
-    if (!isOpen())
-        return;
-        
-    // 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();
+    ASSERT_NOT_SYNC_THREAD();
     
-    deleteAllValues(m_iconURLToIconDataCacheMap);
-    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);
+    if (m_syncThreadRunning) {
+        // Set the flag to tell the sync thread to wrap it up
+        m_threadTerminationRequested = true;
+
+        // Wake up the sync thread if it's waiting
+        wakeSyncThread();
         
-    // 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);
-}
+        // Wait for the sync thread to terminate
+        if (pthread_join(m_syncThread, NULL) == EDEADLK)
+            LOG_ERROR("m_syncThread was found to be deadlocked trying to quit");
+    }
 
-// 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)
-{
-    // 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_imageDataForIconURLStatement = 0;
-    delete m_importedStatement;
-    m_importedStatement = 0;
-    delete m_setImportedStatement;
-    m_setImportedStatement = 0;
-}
-
-bool IconDatabase::isEmpty()
-{
-    if (m_privateBrowsingEnabled)
-        if (!pageURLTableIsEmptyQuery(m_privateBrowsingDB))
-            return false;
-            
-    return pageURLTableIsEmptyQuery(m_mainDB);
+    m_syncThreadRunning = false;    
+    m_threadTerminationRequested = false;
+    m_removeIconsRequested = false;
 }
 
-bool IconDatabase::isValidDatabase(SQLDatabase& db)
+void IconDatabase::removeAllIcons()
 {
-    // These two tables should always exist in a valid db
-    if (!db.tableExists("Icon") || !db.tableExists("PageURL") || !db.tableExists("IconDatabaseInfo"))
-        return false;
-    
-    if (SQLStatement(db, "SELECT value FROM IconDatabaseInfo WHERE key = 'Version';").getColumnInt(0) < currentDatabaseVersion) {
-        LOG(IconDatabase, "DB version is not found or below expected valid version");
-        return false;
-    }
+    ASSERT_NOT_SYNC_THREAD();
     
-    return true;
-}
-
-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);")) {
-        LOG_ERROR("Could not create PageURL table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
-        db.close();
-        return;
-    }
-    if (!db.executeCommand("CREATE TABLE Icon (iconID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE ON CONFLICT REPLACE, url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, stamp INTEGER, data BLOB);")) {
-        LOG_ERROR("Could not create Icon table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
-        db.close();
-        return;
-    }
-    if (!db.executeCommand("CREATE TABLE IconDatabaseInfo (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) {
-        LOG_ERROR("Could not create IconDatabaseInfo table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
-        db.close();
-        return;
-    }
-    if (!db.executeCommand(String("INSERT INTO IconDatabaseInfo VALUES ('Version', ") + String::number(currentDatabaseVersion) + ");")) {
-        LOG_ERROR("Could not insert icon database version into IconDatabaseInfo table (%i) - %s", db.lastError(), db.lastErrorMsg());
-        db.close();
+    if (!isOpen())
         return;
-    }
-}    
 
-PassRefPtr<SharedBuffer> IconDatabase::imageDataForIconURL(const String& iconURL)
-{      
-    // If private browsing is enabled, we'll check there first as the most up-to-date data for an icon will be there
-    if (m_privateBrowsingEnabled) {    
-        RefPtr<SharedBuffer> result = imageDataForIconURLQuery(m_privateBrowsingDB, iconURL);
-        if (result && !result->isEmpty())
-            return result.release();
-    } 
-    
-    // It wasn't found there, so lets check the main tables
-    return imageDataForIconURLQuery(m_mainDB, iconURL);
+    LOG(IconDatabase, "Requesting background thread to remove all icons");
+    m_removeIconsRequested = true;
+    wakeSyncThread();
 }
 
-void IconDatabase::setPrivateBrowsingEnabled(bool flag)
-{
+Image* IconDatabase::iconForPageURL(const String& pageURLOriginal, const IntSize& size, bool cache)
+{   
+    ASSERT_NOT_SYNC_THREAD();
+    
+    // pageURLOriginal can not be stored without being deep copied first.  
+    // We should go our of our way to only copy it if we have to store it
+    
     if (!isOpen())
-        return;
-    if (m_privateBrowsingEnabled == flag)
-        return;
+        return defaultIcon(size);
+                
+    MutexLocker locker(m_urlAndIconLock);
     
-    // Sync any deferred DB changes before we change the active DB
-    syncDatabase();
+    String pageURLCopy; // Creates a null string for easy testing
     
-    m_privateBrowsingEnabled = flag;
+    PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
+    if (!pageRecord) {
+        pageURLCopy = pageURLOriginal.copy();
+        pageRecord = getOrCreatePageURLRecord(pageURLCopy);
+    }
     
-    if (m_privateBrowsingEnabled) {
-        createDatabaseTables(m_privateBrowsingDB);
-        m_currentDB = &m_privateBrowsingDB;
-    } else {
-        m_privateBrowsingDB.clearAllTables();
-        m_currentDB = &m_mainDB;
+    // If pageRecord is NULL, one of two things is true -
+    // 1 - The initial url import is incomplete and this pageURL was marked to be notified once it is complete if an iconURL exists
+    // 2 - The initial url import IS complete and this pageURL has no icon
+    if (!pageRecord) {
+        MutexLocker locker(m_pendingReadingLock);
+        
+        // Import is ongoing, there might be an icon.  In this case, register to be notified when the icon comes in
+        // If we ever reach this condition, we know we've already made the pageURL copy
+        if (!m_iconURLImportComplete)
+            m_pageURLsInterestedInIcons.add(pageURLCopy);
+        
+        return 0;
     }
-}
 
-bool IconDatabase::isPrivateBrowsingEnabled() const
-{
-    return m_privateBrowsingEnabled;
-}
-
-Image* IconDatabase::iconForPageURL(const String& pageURL, const IntSize& size, bool cache)
-{   
-    if (!isOpen())
-        return defaultIcon(size);
-        
-    // See if we even have an IconURL for this PageURL...
-    String iconURL = iconURLForPageURL(pageURL);
-    if (iconURL.isEmpty())
+    IconRecord* iconRecord = pageRecord->iconRecord();
+    
+    // If the initial URL import isn't complete, it's possible to have a PageURL record without an associated icon
+    // In this case, the pageURL is already in the set to alert the client when the iconURL mapping is complete so
+    // we can just bail now
+    if (!m_iconURLImportComplete && !iconRecord)
         return 0;
     
-    // If we do, maybe we have a IconDataCache for this IconURL
-    IconDataCache* icon = getOrCreateIconDataCache(iconURL);
+    // The only way we should *not* have an icon record is if this pageURL is retained but has no icon yet - make sure of that
+    ASSERT(iconRecord || m_retainedPageURLs.contains(pageURLOriginal));
     
-    // If it's a new IconDataCache object that doesn't have its imageData set yet,
-    // we'll read in that image data now
-    if (icon->imageDataStatus() == ImageDataStatusUnknown) {
-        RefPtr<SharedBuffer> data = imageDataForIconURL(iconURL);
-        icon->setImageData(data.get());
+    if (!iconRecord)
+        return 0;
+        
+    // If it's a new IconRecord object that doesn't have its imageData set yet,
+    // mark it to be read by the background thread
+    if (iconRecord->imageDataStatus() == ImageDataStatusUnknown) {
+        if (pageURLCopy.isNull())
+            pageURLCopy = pageURLOriginal.copy();
+    
+        MutexLocker locker(m_pendingReadingLock);
+        m_pageURLsInterestedInIcons.add(pageURLCopy);
+        m_iconsPendingReading.add(iconRecord);
+        wakeSyncThread();
+        return 0;
     }
+    
+    // If the size parameter was (0, 0) that means the caller of this method just wanted the read from disk to be kicked off
+    // and isn't actually interested in the image return value
+    if (size == IntSize(0, 0))
+        return 0;
         
-    return icon->getImage(size);
+    // PARANOID DISCUSSION: This method makes some assumptions.  It returns a WebCore::image which the icon database might dispose of at anytime in the future,
+    // and Images aren't ref counted.  So there is no way for the client to guarantee continued existence of the image.
+    // This has *always* been the case, but in practice clients would always create some other platform specific representation of the image
+    // and drop the raw Image*.  On Mac an NSImage, and on windows drawing into an HBITMAP.
+    // The async aspect adds a huge question - what if the image is deleted before the platform specific API has a chance to create its own
+    // representation out of it?
+    // If an image is read in from the icondatabase, we do *not* overwrite any image data that exists in the in-memory cache.  
+    // This is because we make the assumption that anything in memory is newer than whatever is in the database.
+    // So the only time the data will be set from the second thread is when it is INITIALLY being read in from the database, but we would never 
+    // delete the image on the secondary thread if the image already exists.
+    return iconRecord->image(size);
 }
 
-// FIXME 4667425 - this check needs to see if the icon's data is empty or not and apply
-// iconExpirationTime to present icons, and missingIconExpirationTime for missing icons
-bool IconDatabase::isIconExpiredForIconURL(const String& iconURL)
+void IconDatabase::readIconForPageURLFromDisk(const String& pageURL)
 {
-    // If we're closed and someone is making this call, it is likely a return value of 
-    // false will discourage them to take any further action, which is our goal in this case
-    // Same notion for an empty iconURL - which is now defined as "never expires"
-    if (!isOpen() || iconURL.isEmpty())
-        return false;
-    
-    // If we have a IconDataCache, then it definitely has the Timestamp in it
-    IconDataCache* icon = m_iconURLToIconDataCacheMap.get(iconURL);
-    if (icon) 
-        return (int)currentTime() - icon->getTimestamp() > iconExpirationTime;
-            
-    // Otherwise, we'll get the timestamp from the DB and use it
-    int stamp;
-    if (m_privateBrowsingEnabled) {
-        stamp = timeStampForIconURLQuery(m_privateBrowsingDB, iconURL);
-        if (stamp)
-            return ((int)currentTime() - stamp) > iconExpirationTime;
-    }
-    
-    stamp = timeStampForIconURLQuery(m_mainDB, iconURL);
-    if (stamp)
-        return ((int)currentTime() - stamp) > iconExpirationTime;
-    
-    return false;
+    // The effect of asking for an Icon for a pageURL automatically queues it to be read from disk
+    // if it hasn't already been set in memory.  The special IntSize (0, 0) is a special way of telling 
+    // that method "I don't care about the actual Image, i just want you to make sure you're getting it from disk.
+    iconForPageURL(pageURL, IntSize(0,0));
 }
-    
-String IconDatabase::iconURLForPageURL(const String& pageURL)
+
+String IconDatabase::iconURLForPageURL(const String& pageURLOriginal)
 {    
-    if (!isOpen() || pageURL.isEmpty())
+    ASSERT_NOT_SYNC_THREAD(); 
+        
+    // Cannot do anything with pageURLOriginal that would end up storing it without deep copying first
+    // Also, in the case we have a real answer for the caller, we must deep copy that as well
+    
+    if (!isOpen() || pageURLOriginal.isEmpty())
         return String();
         
-    if (m_pageURLToIconURLMap.contains(pageURL))
-        return m_pageURLToIconURLMap.get(pageURL);
+    MutexLocker locker(m_urlAndIconLock);
     
-    // Try the private browsing database because if any PageURL's IconURL was updated during privated browsing, 
-    // the most up-to-date iconURL would be there
-    if (m_privateBrowsingEnabled) {
-        String iconURL = iconURLForPageURLQuery(m_privateBrowsingDB, pageURL);
-        if (!iconURL.isEmpty()) {
-            m_pageURLToIconURLMap.set(pageURL, iconURL);
-            return iconURL;
-        }
-    }
+    PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
+    if (!pageRecord)
+        pageRecord = getOrCreatePageURLRecord(pageURLOriginal.copy());
+    
+    // If pageRecord is NULL, one of two things is true -
+    // 1 - The initial url import is incomplete and this pageURL has already been marked to be notified once it is complete if an iconURL exists
+    // 2 - The initial url import IS complete and this pageURL has no icon
+    if (!pageRecord)
+        return String();
     
-    String iconURL = iconURLForPageURLQuery(m_mainDB, pageURL);
-    if (!iconURL.isEmpty())
-        m_pageURLToIconURLMap.set(pageURL, iconURL);
-    return iconURL;
+    // Possible the pageRecord is around because it's a retained pageURL with no iconURL, so we have to check
+    return pageRecord->iconRecord() ? pageRecord->iconRecord()->iconURL().copy() : String();
 }
 
 Image* IconDatabase::defaultIcon(const IntSize& size)
 {
-    if (!m_defaultIconDataCache) {
-        m_defaultIconDataCache = new IconDataCache("urlIcon");
-        m_defaultIconDataCache->loadImageFromResource("urlIcon");
+    ASSERT_NOT_SYNC_THREAD();
+
+    if (!m_defaultIconRecord) {
+        m_defaultIconRecord = new IconRecord("urlIcon");
+        m_defaultIconRecord->loadImageFromResource("urlIcon");
     }
     
-    return m_defaultIconDataCache->getImage(size);
+    return m_defaultIconRecord->image(size);
 }
 
-void IconDatabase::retainIconForPageURL(const String& pageURL)
-{
-    if (!isOpen() || pageURL.isEmpty())
+
+void IconDatabase::retainIconForPageURL(const String& pageURLOriginal)
+{    
+    ASSERT_NOT_SYNC_THREAD();
+    
+    // Cannot do anything with pageURLOriginal that would end up storing it without deep copying first
+    
+    if (!isOpen() || pageURLOriginal.isEmpty())
         return;
+       
+    MutexLocker locker(m_urlAndIconLock);
+
+    PageURLRecord* record = m_pageURLToRecordMap.get(pageURLOriginal);
+    
+    String pageURL;
     
-    // If we don't have the retain count for this page, we need to setup records of its retain
-    // Otherwise, get the count and increment it
-    int retainCount;
-    if (!(retainCount = m_pageURLToRetainCount.get(pageURL))) {
-        m_pageURLToRetainCount.set(pageURL, 1);   
+    if (!record) {
+        pageURL = pageURLOriginal.copy();
 
-        // If we haven't done pruning yet, we want to avoid any pageURL->iconURL lookups and the pageURLsPendingDeletion is moot, 
+        record = new PageURLRecord(pageURL);
+        m_pageURLToRecordMap.set(pageURL, record);
+        m_retainedPageURLs.add(pageURL);
+    }
+    
+    if (record->retain()) {
+        // If we read the iconURLs yet, we want to avoid any pageURL->iconURL lookups and the pageURLsPendingDeletion is moot, 
         // so we bail here and skip those steps
-        if (!m_initialPruningComplete)
+        if (!m_iconURLImportComplete)
             return;
-        
-        // If this pageURL is marked for deletion, bring it back from the brink
-        m_pageURLsPendingDeletion.remove(pageURL);
-        
-        // If we have an iconURL for this pageURL, we'll now retain the iconURL
-        String iconURL = iconURLForPageURL(pageURL);
-        if (!iconURL.isEmpty())
-            retainIconURL(iconURL);
 
-    } else
-        m_pageURLToRetainCount.set(pageURL, retainCount + 1);   
+        MutexLocker locker(m_pendingSyncLock);
+        // If this pageURL waiting to be sync'ed, update the sync record
+        // This saves us in the case where a page was ready to be deleted from the database but was just retained - so theres no need to delete it!
+        if (!m_privateBrowsingEnabled && m_pageURLsPendingSync.contains(pageURLOriginal)) {
+            if (pageURL.isNull())
+                pageURL = pageURLOriginal.copy();
+        
+            LOG(IconDatabase, "Bringing %s back from the brink", pageURL.utf8().data());
+            m_pageURLsPendingSync.set(pageURL, record->snapshot());
+        }
+    }
 }
 
-void IconDatabase::releaseIconForPageURL(const String& pageURL)
+void IconDatabase::releaseIconForPageURL(const String& pageURLOriginal)
 {
-    if (!isOpen() || pageURL.isEmpty())
-        return;
+    ASSERT_NOT_SYNC_THREAD();
         
+    // Cannot do anything with pageURLOriginal that would end up storing it without deep copying first
+    
+    if (!isOpen() || pageURLOriginal.isEmpty())
+        return;
+    
+    MutexLocker locker(m_urlAndIconLock);
+
     // Check if this pageURL is actually retained
-    if (!m_pageURLToRetainCount.contains(pageURL)) {
-        LOG_ERROR("Attempting to release icon for URL %s which is not retained", pageURL.ascii().data());
+    if (!m_retainedPageURLs.contains(pageURLOriginal)) {
+        LOG_ERROR("Attempting to release icon for URL %s which is not retained", urlForLogging(pageURLOriginal).utf8().data());
         return;
     }
     
-    // Get its retain count
-    int retainCount = m_pageURLToRetainCount.get(pageURL);
-    ASSERT(retainCount > 0);
-    
+    // Get its retain count - if it's retained, we'd better have a PageURLRecord for it
+    PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
+    ASSERT(pageRecord);
+    LOG(IconDatabase, "Releasing pageURL %s to a retain count of %i", urlForLogging(pageURLOriginal).utf8().data(), pageRecord->retainCount() - 1);
+    ASSERT(pageRecord->retainCount() > 0);
+        
     // If it still has a positive retain count, store the new count and bail
-    if (--retainCount) {
-        m_pageURLToRetainCount.set(pageURL, retainCount);
+    if (pageRecord->release())
         return;
-    }
-    
-    LOG(IconDatabase, "No more retainers for PageURL %s", pageURL.ascii().data());
+        
+    // This pageRecord has now been fully released.  Do the appropriate cleanup
+    LOG(IconDatabase, "No more retainers for PageURL %s", urlForLogging(pageURLOriginal).utf8().data());
+    m_pageURLToRecordMap.remove(pageURLOriginal);
+    m_retainedPageURLs.remove(pageURLOriginal);       
     
-    // Otherwise, remove all record of the retain count
-    m_pageURLToRetainCount.remove(pageURL);   
+    // Grab the iconRecord for later use (and do a sanity check on it for kicks)
+    IconRecord* iconRecord = pageRecord->iconRecord();
     
-    // If we haven't done pruning yet, we want to avoid any pageURL->iconURL lookups and the pageURLsPendingDeletion is moot, 
-    // so we bail here and skip those steps
-    if (!m_initialPruningComplete)
-        return;
+    ASSERT(!iconRecord || (iconRecord && m_iconURLToRecordMap.get(iconRecord->iconURL()) == iconRecord));
 
+    {
+        MutexLocker locker(m_pendingReadingLock);
+        
+        // Since this pageURL is going away, there's no reason anyone would ever be interested in its read results    
+        if (!m_iconURLImportComplete)
+            m_pageURLsPendingImport.remove(pageURLOriginal);
+        m_pageURLsInterestedInIcons.remove(pageURLOriginal);
+        
+        // If this icon is down to it's last retainer, we don't care about reading it in from disk anymore
+        if (iconRecord && iconRecord->hasOneRef()) {
+            m_iconURLToRecordMap.remove(iconRecord->iconURL());
+            m_iconsPendingReading.remove(iconRecord);
+        }
+    }
     
-    // Then mark this pageURL for deletion
-    m_pageURLsPendingDeletion.add(pageURL);
+    // Mark stuff for deletion from the database only if we're not in private browsing
+    if (!m_privateBrowsingEnabled) {
+        MutexLocker locker(m_pendingSyncLock);
+        m_pageURLsPendingSync.set(pageURLOriginal.copy(), pageRecord->snapshot(true));
     
-    // Grab the iconURL and release it
-    String iconURL = iconURLForPageURL(pageURL);
-    if (!iconURL.isEmpty())
-        releaseIconURL(iconURL);
-}
-
-void IconDatabase::retainIconURL(const String& iconURL)
-{
-    ASSERT(!iconURL.isEmpty());
+        // If this page is the last page to refer to a particular IconRecord, that IconRecord needs to
+        // be marked for deletion
+        if (iconRecord && iconRecord->hasOneRef())
+            m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));
+    }
     
-    if (int retainCount = m_iconURLToRetainCount.get(iconURL)) {
-        ASSERT(retainCount > 0);
-        m_iconURLToRetainCount.set(iconURL, retainCount + 1);
-    } else {
-        m_iconURLToRetainCount.set(iconURL, 1);
-        if (m_iconURLsPendingDeletion.contains(iconURL))
-            m_iconURLsPendingDeletion.remove(iconURL);
-    }   
+    delete pageRecord;
+
+    scheduleOrDeferSyncTimer();
 }
 
-void IconDatabase::releaseIconURL(const String& iconURL)
-{
-    ASSERT(!iconURL.isEmpty());
-        
-    // If the iconURL has no retain count, we can bail
-    if (!m_iconURLToRetainCount.contains(iconURL))
-        return;
+void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> dataOriginal, const String& iconURLOriginal)
+{    
+    ASSERT_NOT_SYNC_THREAD();
     
-    // Otherwise, decrement it
-    int retainCount = m_iconURLToRetainCount.get(iconURL) - 1;
-    ASSERT(retainCount > -1);
+    // Cannot do anything with dataOriginal or iconURLOriginal that would end up storing them without deep copying first
     
-    // If the icon is still retained, store the count and bail
-    if (retainCount) {
-        m_iconURLToRetainCount.set(iconURL, retainCount);
+    if (!isOpen() || iconURLOriginal.isEmpty())
         return;
-    }
     
-    LOG(IconDatabase, "No more retainers for IconURL %s", iconURL.ascii().data());
+    RefPtr<SharedBuffer> data = dataOriginal ? dataOriginal->copy() : 0;
+    String iconURL = iconURLOriginal.copy();
     
-    // Otherwise, this icon is toast.  Remove all traces of its retain count...
-    m_iconURLToRetainCount.remove(iconURL);
-    
-    // And since we delay the actual deletion of icons, so lets add it to that queue
-    m_iconURLsPendingDeletion.add(iconURL);
-}
-
-void IconDatabase::forgetPageURL(const String& pageURL)
-{
-    // Remove the PageURL->IconURL mapping
-    m_pageURLToIconURLMap.remove(pageURL);
+    Vector<String> pageURLs;
+    {
+        MutexLocker locker(m_urlAndIconLock);
     
-    // And remove this pageURL from the DB
-    forgetPageURLQuery(*m_currentDB, pageURL);
-}
+        // If this icon was pending a read, remove it from that set because this new data should override what is on disk
+        IconRecord* icon = m_iconURLToRecordMap.get(iconURL);
+        if (icon) {
+            MutexLocker locker(m_pendingReadingLock);
+            m_iconsPendingReading.remove(icon);
+        } else
+            icon = getOrCreateIconRecord(iconURL);
     
-bool IconDatabase::isIconURLRetained(const String& iconURL)
-{
-    if (iconURL.isEmpty())
-        return false;
+        // Update the data and set the time stamp
+        icon->setImageData(data);
+        icon->setTimestamp((int)currentTime());
+        
+        // Copy the current retaining pageURLs - if any - to notify them of the change
+        pageURLs.appendRange(icon->retainingPageURLs().begin(), icon->retainingPageURLs().end());
         
-    return m_iconURLToRetainCount.contains(iconURL);
+        // Mark the IconRecord as requiring an update to the database only if private browsing is disabled
+        if (!m_privateBrowsingEnabled) {
+            MutexLocker locker(m_pendingSyncLock);
+            m_iconsPendingSync.set(iconURL, icon->snapshot());
+        }
+    }
+
+    // Send notification out regarding all PageURLs that retain this icon
+    // But not if we're on the sync thread because that implies this mapping
+    // comes from the initial import which we don't want notifications for
+    if (!IS_ICON_SYNC_THREAD()) {
+        // Start the timer to commit this change - or further delay the timer if it was already started
+        scheduleOrDeferSyncTimer();
+
+        // Informal testing shows that draining the autorelease pool every 25 iterations is about as low as we can go
+        // before performance starts to drop off, but we don't want to increase this number because then accumulated memory usage will go up        
+        AutodrainedPool pool(25);
+
+        for (unsigned i = 0; i < pageURLs.size(); ++i) {
+            LOG(IconDatabase, "Dispatching notification that retaining pageURL %s has a new icon", urlForLogging(pageURLs[i]).utf8().data());
+            m_client->dispatchDidAddIconForPageURL(pageURLs[i]);
+
+            pool.cycle();
+        }
+    }
 }
 
-void IconDatabase::forgetIconForIconURLFromDatabase(const String& iconURL)
-{
-    if (iconURL.isEmpty())
-        return;
+void IconDatabase::setIconURLForPageURL(const String& iconURLOriginal, const String& pageURLOriginal)
+{    
+    ASSERT_NOT_SYNC_THREAD();
 
-    // For private browsing safety, since this alters the database, we only forget from the current database
-    // If we're in private browsing and the Icon also exists in the main database, it will be pruned on the next startup
-    int64_t iconID = establishIconIDForIconURL(*m_currentDB, iconURL, false);
+    // Cannot do anything with iconURLOriginal or pageURLOriginal that would end up storing them without deep copying first
     
-    // If we didn't actually have an icon for this iconURL... well, thats a screwy condition we should track down, but also 
-    // something we could move on from
-    ASSERT(iconID);
-    if (!iconID) {
-        LOG_ERROR("Attempting to forget icon for IconURL %s, though we don't have it in the database", iconURL.ascii().data());
+    ASSERT(!iconURLOriginal.isEmpty());
+        
+    if (!isOpen() || pageURLOriginal.isEmpty())
         return;
-    }
     
-    if (!m_currentDB->executeCommand(String::format("DELETE FROM Icon WHERE Icon.iconID = %lli;", iconID)))
-        LOG_ERROR("Unable to drop Icon for IconURL", iconURL.ascii().data()); 
-    if (!m_currentDB->executeCommand(String::format("DELETE FROM PageURL WHERE PageURL.iconID = %lli", iconID)))
-        LOG_ERROR("Unable to drop all PageURL for IconURL", iconURL.ascii().data()); 
-}
+    String iconURL, pageURL;
+    
+    {
+        MutexLocker locker(m_urlAndIconLock);
 
-IconDataCache* IconDatabase::getOrCreateIconDataCache(const String& iconURL)
-{
-    IconDataCache* icon;
-    if ((icon = m_iconURLToIconDataCacheMap.get(iconURL)))
-        return icon;
+        PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURLOriginal);
         
-    icon = new IconDataCache(iconURL);
-    m_iconURLToIconDataCacheMap.set(iconURL, icon);
-    
-    // Get the most current time stamp for this IconURL
-    int timestamp = 0;
-    if (m_privateBrowsingEnabled)
-        timestamp = timeStampForIconURLQuery(m_privateBrowsingDB, iconURL);
-    if (!timestamp)
-        timestamp = timeStampForIconURLQuery(m_mainDB, iconURL);
+        // If the urls already map to each other, bail.
+        // This happens surprisingly often, and seems to cream iBench performance
+        if (pageRecord && pageRecord->iconRecord() && pageRecord->iconRecord()->iconURL() == iconURLOriginal)
+            return;
+            
+        pageURL = pageURLOriginal.copy();
+        iconURL = iconURLOriginal.copy();
+
+        if (!pageRecord) {
+            pageRecord = new PageURLRecord(pageURL);
+            m_pageURLToRecordMap.set(pageURL, pageRecord);
+        }
+
+        RefPtr<IconRecord> iconRecord = pageRecord->iconRecord();
+
+        // Otherwise, set the new icon record for this page
+        IconRecord* newIconRecord = getOrCreateIconRecord(iconURL);
+        pageRecord->setIconRecord(newIconRecord);
+
+        // If the current icon has only a single ref left, it is about to get wiped out. 
+        // Remove it from the in-memory records and don't bother reading it in from disk anymore
+        if (iconRecord && iconRecord->hasOneRef()) {
+            ASSERT(iconRecord->retainingPageURLs().size() == 0);
+            LOG(IconDatabase, "Icon for icon url %s is about to be destroyed - removing mapping for it", urlForLogging(iconRecord->iconURL()).utf8().data());
+            m_iconURLToRecordMap.remove(iconRecord->iconURL());
+            MutexLocker locker(m_pendingReadingLock);
+            m_iconsPendingReading.remove(iconRecord.get());
+        }
         
-    // If we can't get a timestamp for this URL, then it is a new icon and we initialize its timestamp now
-    if (!timestamp) {
-        icon->setTimestamp((int)currentTime());
-        m_iconDataCachesPendingUpdate.add(icon);
-    } else 
-        icon->setTimestamp(timestamp);
+        // And mark this mapping to be added to the database
+        if (!m_privateBrowsingEnabled) {
+            MutexLocker locker(m_pendingSyncLock);
+            m_pageURLsPendingSync.set(pageURL, pageRecord->snapshot());
+            
+            // If the icon is on it's last ref, mark it for deletion
+            if (iconRecord && iconRecord->hasOneRef())
+                m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));
+        }
+    }
+
+    // Since this mapping is new, send the notification out - but not if we're on the sync thread because that implies this mapping
+    // comes from the initial import which we don't want notifications for
+    if (!IS_ICON_SYNC_THREAD()) {
+        // Start the timer to commit this change - or further delay the timer if it was already started
+        scheduleOrDeferSyncTimer();
         
-    return icon;
+        LOG(IconDatabase, "Dispatching notification that we changed an icon mapping for url %s", urlForLogging(pageURL).utf8().data());
+        AutodrainedPool pool;
+        m_client->dispatchDidAddIconForPageURL(pageURL);
+    }
 }
 
-void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> data, const String& iconURL)
+IconLoadDecision IconDatabase::loadDecisionForIconURL(const String& iconURL, DocumentLoader* notificationDocumentLoader)
 {
-    if (!isOpen() || iconURL.isEmpty())
-        return;
+    ASSERT_NOT_SYNC_THREAD();
 
-    // Get the IconDataCache for this IconURL (note, IconDataCacheForIconURL will create it if necessary)
-    IconDataCache* icon = getOrCreateIconDataCache(iconURL);
-    
-    // Set the data in the IconDataCache
-    icon->setImageData(data);
+    if (!isOpen() || iconURL.isEmpty())
+        return IconLoadNo;
+    
+    // If we have a IconRecord, it should also have its timeStamp marked because there is only two times when we create the IconRecord:
+    // 1 - When we read the icon urls from disk, getting the timeStamp at the same time
+    // 2 - When we get a new icon from the loader, in which case the timestamp is set at that time
+    {
+        MutexLocker locker(m_urlAndIconLock);
+        if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL)) {
+            LOG(IconDatabase, "Found expiration time on a present icon based on existing IconRecord");
+            return (int)currentTime() - icon->getTimestamp() > iconExpirationTime ? IconLoadYes : IconLoadNo;
+        }
+    }
     
-    // Update the timestamp in the IconDataCache to NOW
-    icon->setTimestamp((int)currentTime());
+    // If we don't have a record for it, but we *have* imported all iconURLs from disk, then we should load it now
+    MutexLocker readingLocker(m_pendingReadingLock);
+    if (m_iconURLImportComplete)
+        return IconLoadYes;
+        
+    // Otherwise - since we refuse to perform I/O on the main thread to find out for sure - we return the answer that says
+    // "You might be asked to load this later, so flag that"
+    LOG(IconDatabase, "Don't know if we should load %s or not - adding %p to the set of document loaders waiting on a decision", iconURL.utf8().data(), notificationDocumentLoader);
+    m_loadersPendingDecision.add(notificationDocumentLoader);    
 
-    // Mark the IconDataCache as requiring an update to the database
-    m_iconDataCachesPendingUpdate.add(icon);
+    return IconLoadUnknown;
 }
 
-void IconDatabase::setHaveNoIconForIconURL(const String& iconURL)
-{   
-    setIconDataForIconURL(0, iconURL);
+bool IconDatabase::iconDataKnownForIconURL(const String& iconURL)
+{
+    ASSERT_NOT_SYNC_THREAD();
+    
+    MutexLocker locker(m_urlAndIconLock);
+    if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL))
+        return icon->imageDataStatus() != ImageDataStatusUnknown;
+
+    return false;
 }
 
-bool IconDatabase::setIconURLForPageURL(const String& iconURL, const String& pageURL)
+void IconDatabase::setEnabled(bool enabled)
 {
-    ASSERT(!iconURL.isEmpty());
-    if (!isOpen() || pageURL.isEmpty())
-        return false;
+    ASSERT_NOT_SYNC_THREAD();
     
-    // If the urls already map to each other, bail.
-    // This happens surprisingly often, and seems to cream iBench performance
-    if (m_pageURLToIconURLMap.get(pageURL) == iconURL)
-        return false;
-
-    // If this pageURL is retained, we have some work to do on the IconURL retain counts
-    if (m_pageURLToRetainCount.contains(pageURL)) {
-        String oldIconURL = m_pageURLToIconURLMap.get(pageURL);
-        if (!oldIconURL.isEmpty())
-            releaseIconURL(oldIconURL);
-        retainIconURL(iconURL);
-    } else {
-        // If this pageURL is *not* retained, then we may be marking it for deletion, as well!
-        // As counterintuitive as it seems to mark it for addition and for deletion at the same time,
-        // it's valid because when we do a new pageURL->iconURL mapping we *have* to mark it for addition,
-        // no matter what, as there is no efficient was to determine if the mapping is in the DB already.
-        // But, if the iconURL is marked for deletion, we'll also mark this pageURL for deletion - if a 
-        // client comes along and retains it before the timer fires, the "pendingDeletion" lists will
-        // be manipulated appopriately and new pageURL will be brought back from the brink
-        if (m_iconURLsPendingDeletion.contains(iconURL))
-            m_pageURLsPendingDeletion.add(pageURL);
+    if (!enabled && isOpen())
+        close();
+    m_isEnabled = enabled;
+}
+
+bool IconDatabase::isEnabled() const
+{
+    ASSERT_NOT_SYNC_THREAD();
+    
+     return m_isEnabled;
+}
+
+void IconDatabase::setPrivateBrowsingEnabled(bool flag)
+{
+    m_privateBrowsingEnabled = flag;
+}
+
+bool IconDatabase::isPrivateBrowsingEnabled() const
+{
+    return m_privateBrowsingEnabled;
+}
+
+void IconDatabase::delayDatabaseCleanup()
+{
+    ++databaseCleanupCounter;
+    if (databaseCleanupCounter == 1)
+        LOG(IconDatabase, "Database cleanup is now DISABLED");
+}
+
+void IconDatabase::allowDatabaseCleanup()
+{
+    if (--databaseCleanupCounter < 0)
+        databaseCleanupCounter = 0;
+    if (databaseCleanupCounter == 0)
+        LOG(IconDatabase, "Database cleanup is now ENABLED");
+}
+
+void IconDatabase::checkIntegrityBeforeOpening()
+{
+    checkIntegrityOnOpen = true;
+}
+
+size_t IconDatabase::pageURLMappingCount()
+{
+    MutexLocker locker(m_urlAndIconLock);
+    return m_pageURLToRecordMap.size();
+}
+
+size_t IconDatabase::retainedPageURLCount()
+{
+    MutexLocker locker(m_urlAndIconLock);
+    return m_retainedPageURLs.size();
+}
+
+size_t IconDatabase::iconRecordCount()
+{
+    MutexLocker locker(m_urlAndIconLock);
+    return m_iconURLToRecordMap.size();
+}
+
+size_t IconDatabase::iconRecordCountWithData()
+{
+    MutexLocker locker(m_urlAndIconLock);
+    size_t result = 0;
+    
+    HashMap<String, IconRecord*>::iterator i = m_iconURLToRecordMap.begin();
+    HashMap<String, IconRecord*>::iterator end = m_iconURLToRecordMap.end();
+    
+    for (; i != end; ++i)
+        result += ((*i).second->imageDataStatus() == ImageDataStatusPresent);
+            
+    return result;
+}
+
+IconDatabase::IconDatabase()
+    : m_syncTimer(this, &IconDatabase::syncTimerFired)
+    , m_syncThreadRunning(false)
+    , m_defaultIconRecord(0)
+    , m_isEnabled(false)
+    , m_privateBrowsingEnabled(false)
+    , m_threadTerminationRequested(false)
+    , m_removeIconsRequested(false)
+    , m_iconURLImportComplete(false)
+    , m_initialPruningComplete(false)
+    , m_client(defaultClient())
+    , m_imported(false)
+    , m_isImportedSet(false)
+{
+    ASSERT(pthread_main_np());
+}
+
+IconDatabase::~IconDatabase()
+{
+    ASSERT_NOT_REACHED();
+}
+
+void IconDatabase::notifyPendingLoadDecisions()
+{    
+    iconDatabase()->notifyPendingLoadDecisionsInternal();
+}
+
+void IconDatabase::notifyPendingLoadDecisionsInternal()
+{
+    ASSERT_NOT_SYNC_THREAD();
+    
+    // This method should only be called upon completion of the initial url import from the database
+    ASSERT(m_iconURLImportComplete);
+    LOG(IconDatabase, "Notifying all DocumentLoaders that were waiting on a load decision for thier icons");
+    
+    HashSet<RefPtr<DocumentLoader> >::iterator i = m_loadersPendingDecision.begin();
+    HashSet<RefPtr<DocumentLoader> >::iterator end = m_loadersPendingDecision.end();
+    
+    for (; i != end; ++i)
+        if ((*i)->refCount() > 1)
+            (*i)->iconLoadDecisionAvailable();
+            
+    m_loadersPendingDecision.clear();
+}
+
+void IconDatabase::wakeSyncThread()
+{
+    MutexLocker locker(m_syncLock);
+    m_syncCondition.signal();
+}
+
+void IconDatabase::scheduleOrDeferSyncTimer()
+{
+    ASSERT_NOT_SYNC_THREAD();
+    
+    m_syncTimer.startOneShot(updateTimerDelay);
+}
+
+void IconDatabase::syncTimerFired(Timer<IconDatabase>*)
+{
+    ASSERT_NOT_SYNC_THREAD();
+    wakeSyncThread();
+}
+
+// ******************
+// *** Any Thread ***
+// ******************
+
+bool IconDatabase::isOpen() const
+{
+    MutexLocker locker(m_syncLock);
+    return m_syncDB.isOpen();
+}
+
+String IconDatabase::databasePath() const
+{
+    MutexLocker locker(m_syncLock);
+    return m_completeDatabasePath.copy();
+}
+
+String IconDatabase::defaultDatabaseFilename()
+{
+    static String defaultDatabaseFilename = "Icons.db";
+    return defaultDatabaseFilename.copy();
+}
+
+// Unlike getOrCreatePageURLRecord(), getOrCreateIconRecord() does not mark the icon as "interested in import"
+IconRecord* IconDatabase::getOrCreateIconRecord(const String& iconURL)
+{
+    // Clients of getOrCreateIconRecord() are required to acquire the m_urlAndIconLock before calling this method
+    ASSERT(m_urlAndIconLock.tryLock() == EBUSY);
+
+    if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL))
+        return icon;
+        
+    IconRecord* newIcon = new IconRecord(iconURL);
+    m_iconURLToRecordMap.set(iconURL, newIcon);
+        
+    return newIcon;    
+}
+
+// This method retrieves the existing PageURLRecord, or creates a new one and marks it as "interested in the import" for later notification
+PageURLRecord* IconDatabase::getOrCreatePageURLRecord(const String& pageURL)
+{
+    // Clients of getOrCreatePageURLRecord() are required to acquire the m_urlAndIconLock before calling this method
+    ASSERT(m_urlAndIconLock.tryLock() == EBUSY);
+
+    PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURL);
+    
+    MutexLocker locker(m_pendingReadingLock);
+    if (!m_iconURLImportComplete) {
+        // If the initial import of all URLs hasn't completed and we have no page record, we assume we *might* know about this later and create a record for it
+        if (!pageRecord) {
+            LOG(IconDatabase, "Creating new PageURLRecord for pageURL %s", urlForLogging(pageURL).utf8().data());
+            pageRecord = new PageURLRecord(pageURL);
+            m_pageURLToRecordMap.set(pageURL, pageRecord);
+        }
+
+        // If the pageRecord for this page does not have an iconRecord attached to it, then it is a new pageRecord still awaiting the initial import
+        // Mark the URL as "interested in the result of the import" then bail
+        if (!pageRecord->iconRecord()) {
+            m_pageURLsPendingImport.add(pageURL);
+            return 0;
+        }
     }
+
+    // We've done the initial import of all URLs known in the database.  If this record doesn't exist now, it never will    
+     return pageRecord;
+}
+
+
+// ************************
+// *** Sync Thread Only ***
+// ************************
+
+void IconDatabase::importIconURLForPageURL(const String& iconURL, const String& pageURL)
+{
+    ASSERT_ICON_SYNC_THREAD();
+    
+    // This function is only for setting actual existing url mappings so assert that neither of these URLs are empty
+    ASSERT(!iconURL.isEmpty() && !pageURL.isEmpty());
+    
+    setIconURLForPageURLInSQLDatabase(iconURL, pageURL);    
+}
+
+void IconDatabase::importIconDataForIconURL(PassRefPtr<SharedBuffer> data, const String& iconURL)
+{
+    ASSERT_ICON_SYNC_THREAD();
     
-    // Cache the pageURL->iconURL map
-    m_pageURLToIconURLMap.set(pageURL, iconURL);
+    ASSERT(!iconURL.isEmpty());
+
+    writeIconSnapshotToSQLDatabase(IconSnapshot(iconURL, (int)currentTime(), data.get()));
+}
+
+bool IconDatabase::shouldStopThreadActivity() const
+{
+    ASSERT_ICON_SYNC_THREAD();
     
-    // And mark this mapping to be added to the database
-    m_pageURLsPendingAddition.add(pageURL);
+    return m_threadTerminationRequested || m_removeIconsRequested;
+}
+
+void* IconDatabase::iconDatabaseSyncThreadStart(void* vIconDatabase)
+{    
+    IconDatabase* iconDB = static_cast<IconDatabase*>(vIconDatabase);
     
-    // Then start the timer to commit this change - or further delay the timer if it
-    // was already started
-    m_updateTimer.startOneShot(updateTimerDelay);
+    return iconDB->iconDatabaseSyncThread();
+}
+
+void* IconDatabase::iconDatabaseSyncThread()
+{
+    // The call to create this thread might not complete before the thread actually starts, so we might fail this ASSERT_ICON_SYNC_THREAD() because the pointer 
+    // to our thread structure hasn't been filled in yet.
+    // To fix this, the main thread acquires this lock before creating us, then releases the lock after creation is complete.  A quick lock/unlock cycle here will 
+    // prevent us from running before that call completes
+    m_syncLock.lock();
+    m_syncLock.unlock();
+
+    ASSERT_ICON_SYNC_THREAD();
+    
+    LOG(IconDatabase, "(THREAD) IconDatabase sync thread started");
+
+#ifndef NDEBUG
+    double startTime = currentTime();
+#endif
+
+    // Existence of a journal file is evidence of a previous crash/force quit and automatically qualifies
+    // us to do an integrity check
+    String journalFilename = m_completeDatabasePath + "-journal";
+    if (!checkIntegrityOnOpen)
+        checkIntegrityOnOpen = fileExists(journalFilename);
+        
+    {
+        MutexLocker locker(m_syncLock);
+        if (!m_syncDB.open(m_completeDatabasePath)) {
+            LOG_ERROR("Unable to open icon database at path %s - %s", m_completeDatabasePath.utf8().data(), m_syncDB.lastErrorMsg());
+            return 0;
+        }
+    }
+    
+    if (shouldStopThreadActivity())
+        return syncThreadMainLoop();
+        
+#ifndef NDEBUG
+    double timeStamp = currentTime();
+    LOG(IconDatabase, "(THREAD) Open took %.4f seconds", timeStamp - startTime);
+#endif    
+
+    performOpenInitialization();
+    if (shouldStopThreadActivity())
+        return syncThreadMainLoop();
+        
+#ifndef NDEBUG
+    double newStamp = currentTime();
+    LOG(IconDatabase, "(THREAD) performOpenInitialization() took %.4f seconds, now %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime);
+    timeStamp = newStamp;
+#endif 
+
+    if (!imported()) {
+        LOG(IconDatabase, "(THREAD) Performing Safari2 import procedure");
+        SQLTransaction importTransaction(m_syncDB);
+        importTransaction.begin();
+        
+        // Commit the transaction only if the import completes (the import should be atomic)
+        if (m_client->performImport()) {
+            setImported(true);
+            importTransaction.commit();
+        } else {
+            LOG(IconDatabase, "(THREAD) Safari 2 import was cancelled");
+            importTransaction.rollback();
+        }
+        
+        if (shouldStopThreadActivity())
+            return syncThreadMainLoop();
+            
+#ifndef NDEBUG
+        newStamp = currentTime();
+        LOG(IconDatabase, "(THREAD) performImport() took %.4f seconds, now %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime);
+        timeStamp = newStamp;
+#endif 
+    }
+        
+    // Uncomment the following line to simulate a long lasting URL import (*HUGE* icon databases, or network home directories)
+    // while (currentTime() - timeStamp < 10);
+
+    // Read in URL mappings from the database          
+    LOG(IconDatabase, "(THREAD) Starting iconURL import");
+    performURLImport();
+    
+    if (shouldStopThreadActivity())
+        return syncThreadMainLoop();
+
+#ifndef NDEBUG
+    newStamp = currentTime();
+    LOG(IconDatabase, "(THREAD) performURLImport() took %.4f seconds.  Entering main loop %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime);
+#endif 
+
+    LOG(IconDatabase, "(THREAD) Beginning sync");
+    return syncThreadMainLoop();
+}
+
+static int databaseVersionNumber(SQLDatabase& db)
+{
+    return SQLStatement(db, "SELECT value FROM IconDatabaseInfo WHERE key = 'Version';").getColumnInt(0);
+}
+
+static bool isValidDatabase(SQLDatabase& db)
+{
+
+    // These four tables should always exist in a valid db
+    if (!db.tableExists("IconInfo") || !db.tableExists("IconData") || !db.tableExists("PageURL") || !db.tableExists("IconDatabaseInfo"))
+        return false;
+    
+    if (databaseVersionNumber(db) != currentDatabaseVersion) {
+        LOG(IconDatabase, "DB version is not found or below expected valid version");
+        return false;
+    }
     
     return true;
 }
 
-void IconDatabase::setIconURLForPageURLInDatabase(const String& iconURL, const String& pageURL)
-{
-    int64_t iconID = establishIconIDForIconURL(*m_currentDB, iconURL);
-    if (!iconID) {
-        LOG_ERROR("Failed to establish an ID for iconURL %s", iconURL.ascii().data());
-        return;
-    }
-    setIconIDForPageURLQuery(*m_currentDB, iconID, pageURL);
-}
+static void 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);")) {
+        LOG_ERROR("Could not create PageURL table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+        db.close();
+        return;
+    }
+    if (!db.executeCommand("CREATE INDEX PageURLIndex ON PageURL (url);")) {
+        LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+        db.close();
+        return;
+    }
+    if (!db.executeCommand("CREATE TABLE IconInfo (iconID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE ON CONFLICT REPLACE, url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, stamp INTEGER);")) {
+        LOG_ERROR("Could not create IconInfo table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+        db.close();
+        return;
+    }
+    if (!db.executeCommand("CREATE INDEX IconInfoIndex ON IconInfo (url, iconID);")) {
+        LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+        db.close();
+        return;
+    }
+    if (!db.executeCommand("CREATE TABLE IconData (iconID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE ON CONFLICT REPLACE, data BLOB);")) {
+        LOG_ERROR("Could not create IconData table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+        db.close();
+        return;
+    }
+    if (!db.executeCommand("CREATE INDEX IconDataIndex ON IconData (iconID);")) {
+        LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+        db.close();
+        return;
+    }
+    if (!db.executeCommand("CREATE TABLE IconDatabaseInfo (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) {
+        LOG_ERROR("Could not create IconDatabaseInfo table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
+        db.close();
+        return;
+    }
+    if (!db.executeCommand(String("INSERT INTO IconDatabaseInfo VALUES ('Version', ") + String::number(currentDatabaseVersion) + ");")) {
+        LOG_ERROR("Could not insert icon database version into IconDatabaseInfo table (%i) - %s", db.lastError(), db.lastErrorMsg());
+        db.close();
+        return;
+    }
+}    
+
+void IconDatabase::performOpenInitialization()
+{
+    ASSERT_ICON_SYNC_THREAD();
+    
+    if (!isOpen())
+        return;
+    
+    if (checkIntegrityOnOpen) {
+        checkIntegrityOnOpen = false;
+        if (!checkIntegrity()) {
+            LOG(IconDatabase, "Integrity check was bad - dumping IconDatabase");
+
+            close();
+            
+            {
+                MutexLocker locker(m_syncLock);
+                // Should've been consumed by SQLite, delete just to make sure we don't see it again in the future;
+                deleteFile(m_completeDatabasePath + "-journal");
+                deleteFile(m_completeDatabasePath);
+            }
+            
+            // Reopen the write database, creating it from scratch
+            if (!m_syncDB.open(m_completeDatabasePath)) {
+                LOG_ERROR("Unable to open icon database at path %s - %s", m_completeDatabasePath.utf8().data(), m_syncDB.lastErrorMsg());
+                return;
+            }          
+        }
+    }
+        
+    if (!isValidDatabase(m_syncDB)) {
+        LOG(IconDatabase, "%s is missing or in an invalid state - reconstructing", m_syncDB.path().utf8().data());
+        m_syncDB.clearAllTables();
+        createDatabaseTables(m_syncDB);
+    }
+
+    // Reduce sqlite RAM cache size from default 2000 pages (~1.5kB per page). 3MB of cache for icon database is overkill
+    if (!SQLStatement(m_syncDB, "PRAGMA cache_size = 200;").executeCommand())         
+        LOG_ERROR("SQLite database could not set cache_size");
+}
+
+bool IconDatabase::checkIntegrity()
+{
+    ASSERT_ICON_SYNC_THREAD();
+    
+    SQLStatement integrity(m_syncDB, "PRAGMA integrity_check;");
+    if (integrity.prepare() != SQLResultOk) {
+        LOG_ERROR("checkIntegrity failed to execute");
+        return false;
+    }
+    
+    int resultCode = integrity.step();
+    if (resultCode == SQLResultOk)
+        return true;
+        
+    if (resultCode != SQLResultRow)
+        return false;
+
+    int columns = integrity.columnCount();
+    if (columns != 1) {
+        LOG_ERROR("Received %i columns performing integrity check, should be 1", columns);
+        return false;
+    }
+        
+    String resultText = integrity.getColumnText16(0);
+        
+    // A successful, no-error integrity check will be "ok" - all other strings imply failure
+    if (resultText == "ok")
+        return true;
+    
+    LOG_ERROR("Icon database integrity check failed - \n%s", resultText.utf8().data());
+    return false;
+}
+
+void IconDatabase::performURLImport()
+{
+    ASSERT_ICON_SYNC_THREAD();
+
+    SQLStatement query(m_syncDB, "SELECT PageURL.url, IconInfo.url, IconInfo.stamp FROM PageURL INNER JOIN IconInfo ON PageURL.iconID=IconInfo.iconID;");
+    
+    if (query.prepare() != SQLResultOk) {
+        LOG_ERROR("Unable to prepare icon url import query");
+        return;
+    }
+    
+    // Informal testing shows that draining the autorelease pool every 25 iterations is about as low as we can go
+    // before performance starts to drop off, but we don't want to increase this number because then accumulated memory usage will go up
+    AutodrainedPool pool(25);
+        
+    int result = query.step();
+    while (result == SQLResultRow) {
+        String pageURL = query.getColumnText16(0);
+        String iconURL = query.getColumnText16(1);
+
+        {
+            MutexLocker locker(m_urlAndIconLock);
+            
+            PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURL);
+            
+            // If the pageRecord doesn't exist in this map, then no one has retained this pageURL
+            // If the s_databaseCleanupCounter count is non-zero, then we're not supposed to be pruning the database in any manner,
+            // so go ahead and actually create a pageURLRecord for this url even though it's not retained.
+            // If database cleanup *is* allowed, we don't want to bother pulling in a page url from disk that noone is actually interested
+            // in - we'll prune it later instead!
+            if (!pageRecord && databaseCleanupCounter) {
+                pageRecord = new PageURLRecord(pageURL);
+                m_pageURLToRecordMap.set(pageURL, pageRecord);
+            }
+            
+            if (pageRecord) {
+                IconRecord* currentIcon = pageRecord->iconRecord();
+
+                if (!currentIcon || currentIcon->iconURL() != iconURL) {
+                    currentIcon = getOrCreateIconRecord(iconURL);
+                    pageRecord->setIconRecord(currentIcon);
+                }
+            
+                // Regardless, the time stamp from disk still takes precedence.  Until we read this icon from disk, we didn't think we'd seen it before
+                // so we marked the timestamp as "now", but it's really much older
+                currentIcon->setTimestamp(query.getColumnInt(2));
+            }            
+        }
+        
+        // FIXME: Currently the WebKit API supports 1 type of notification that is sent whenever we get an Icon URL for a Page URL.  We might want to re-purpose it to work for 
+        // getting the actually icon itself also (so each pageurl would get this notification twice) or we might want to add a second type of notification -
+        // one for the URL and one for the Image itself
+        // Note that WebIconDatabase is not neccessarily API so we might be able to make this change
+        {
+            MutexLocker locker(m_pendingReadingLock);
+            if (m_pageURLsPendingImport.contains(pageURL)) {
+                m_client->dispatchDidAddIconForPageURL(pageURL);
+                m_pageURLsPendingImport.remove(pageURL);
+            
+                pool.cycle();
+            }
+        }
+        
+        // Stop the import at any time of the thread has been asked to shutdown
+        if (shouldStopThreadActivity()) {
+            LOG(IconDatabase, "IconDatabase asked to terminate during performURLImport()");
+            return;
+        }
+        
+        result = query.step();
+    }
+    
+    if (result != SQLResultDone)
+        LOG(IconDatabase, "Error reading page->icon url mappings from database");
+
+    // Clear the m_pageURLsPendingImport set - either the page URLs ended up with an iconURL (that we'll notify about) or not, 
+    // but after m_iconURLImportComplete is set to true, we don't care about this set anymore
+    Vector<String> urls;
+    {
+        MutexLocker locker(m_pendingReadingLock);
+
+        urls.appendRange(m_pageURLsPendingImport.begin(), m_pageURLsPendingImport.end());
+        m_pageURLsPendingImport.clear();        
+        m_iconURLImportComplete = true;
+    }
+    
+    Vector<String> urlsToNotify;
+    
+    // Loop through the urls pending import
+    // Remove unretained ones if database cleanup is allowed
+    // Keep a set of ones that are retained and pending notification
+    
+    {
+        MutexLocker locker(m_urlAndIconLock);
+        
+        for (unsigned i = 0; i < urls.size(); ++i) {
+            if (!m_retainedPageURLs.contains(urls[i])) {
+                PageURLRecord* record = m_pageURLToRecordMap.get(urls[i]);
+                if (record && !databaseCleanupCounter) {
+                    m_pageURLToRecordMap.remove(urls[i]);
+                    IconRecord* iconRecord = record->iconRecord();
+                    
+                    // If this page is the only remaining retainer of its icon, mark that icon for deletion and don't bother
+                    // reading anything related to it 
+                    if (iconRecord && iconRecord->hasOneRef()) {
+                        m_iconURLToRecordMap.remove(iconRecord->iconURL());
+                        
+                        {
+                            MutexLocker locker(m_pendingReadingLock);
+                            m_pageURLsInterestedInIcons.remove(urls[i]);
+                            m_iconsPendingReading.remove(iconRecord);
+                        }
+                        {
+                            MutexLocker locker(m_pendingSyncLock);
+                            m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));                    
+                        }
+                    }
+                    
+                    delete record;
+                }
+            } else {
+                urlsToNotify.append(urls[i]);
+            }
+        }
+    }
+
+    LOG(IconDatabase, "Notifying %i interested page URLs that their icon URL is known due to the import", urlsToNotify.size());
+    // Now that we don't hold any locks, perform the actual notifications
+    for (unsigned i = 0; i < urlsToNotify.size(); ++i) {
+        LOG(IconDatabase, "Notifying icon info known for pageURL %s", urlsToNotify[i].utf8().data());
+        m_client->dispatchDidAddIconForPageURL(urlsToNotify[i]);
+        if (shouldStopThreadActivity())
+            return;
+
+        pool.cycle();
+    }
+    
+    // Notify all DocumentLoaders that were waiting for an icon load decision on the main thread
+    callOnMainThread(notifyPendingLoadDecisions);
+}
+
+void* IconDatabase::syncThreadMainLoop()
+{
+    ASSERT_ICON_SYNC_THREAD();
+    static bool prunedUnretainedIcons = false;
+
+    while (true) {
+        m_syncLock.unlock();
+
+#ifndef NDEBUG
+        double timeStamp = currentTime();
+#endif
+        LOG(IconDatabase, "(THREAD) Main work loop starting");
+
+        // If we should remove all icons, do it now.  This is an uninteruptible procedure that we will always do before quitting if it is requested
+        if (m_removeIconsRequested) {
+            removeAllIconsOnThread();
+            m_removeIconsRequested = false;
+        }
+        
+        // Then, if the thread should be quitting, quit now!
+        if (m_threadTerminationRequested)
+            break;
+        
+        bool didAnyWork = true;
+        while (didAnyWork) {
+            didAnyWork = readFromDatabase();
+            if (shouldStopThreadActivity())
+                break;
+                
+            bool didWrite = writeToDatabase();
+            if (shouldStopThreadActivity())
+                break;
+                
+            // Prune unretained icons after the first time we sync anything out to the database
+            // This way, pruning won't be the only operation we perform to the database by itself
+            // We also don't want to bother doing this if the thread should be terminating (the user is quitting)
+            // or if private browsing is enabled
+            // We also don't want to prune if the m_databaseCleanupCounter count is non-zero - that means someone
+            // has asked to delay pruning
+            if (didWrite && !m_privateBrowsingEnabled && !prunedUnretainedIcons && !databaseCleanupCounter) {
+#ifndef NDEBUG
+                double time = currentTime();
+#endif
+                LOG(IconDatabase, "(THREAD) Starting pruneUnretainedIcons()");
+                
+                pruneUnretainedIcons();
+                
+                LOG(IconDatabase, "(THREAD) pruneUnretainedIcons() took %.4f seconds", currentTime() - time);
+                
+                // If pruneUnretainedIcons() returned early due to requested thread termination, its still okay
+                // to mark prunedUnretainedIcons true because we're about to terminate anyway
+                prunedUnretainedIcons = true;
+            }
+            
+            didAnyWork = didAnyWork || didWrite;
+            if (shouldStopThreadActivity())
+                break;
+        }
+        
+#ifndef NDEBUG
+        double newstamp = currentTime();
+        LOG(IconDatabase, "(THREAD) Main work loop ran for %.4f seconds, %s requested to terminate", newstamp - timeStamp, shouldStopThreadActivity() ? "was" : "was not");
+#endif
+                    
+        m_syncLock.lock();
+        
+        // There is some condition that is asking us to stop what we're doing now and handle a special case
+        // This is either removing all icons, or shutting down the thread to quit the app
+        // We handle those at the top of this main loop so continue to jump back up there
+        if (shouldStopThreadActivity())
+            continue;
+            
+        m_syncCondition.wait(m_syncLock); 
+    }
+    m_syncLock.unlock();
+    
+    // Thread is terminating at this point
+    return cleanupSyncThread();
+}
+
+bool IconDatabase::readFromDatabase()
+{
+    ASSERT_ICON_SYNC_THREAD();
+    
+#ifndef NDEBUG
+    double timeStamp = currentTime();
+#endif
+
+    bool didAnyWork = false;
+
+    // We'll make a copy of the sets of things that need to be read.  Then we'll verify at the time of updating the record that it still wants to be updated
+    // This way we won't hold the lock for a long period of time
+    Vector<IconRecord*> icons;
+    {
+        MutexLocker locker(m_pendingReadingLock);
+        icons.appendRange(m_iconsPendingReading.begin(), m_iconsPendingReading.end());
+    }
+    
+    // Keep track of icons we actually read to notify them of the new icon    
+    HashSet<String> urlsToNotify;
+    
+    for (unsigned i = 0; i < icons.size(); ++i) {
+        didAnyWork = true;
+        RefPtr<SharedBuffer> imageData = getImageDataForIconURLFromSQLDatabase(icons[i]->iconURL());
+
+        // Verify this icon still wants to be read from disk
+        {
+            MutexLocker urlLocker(m_urlAndIconLock);
+            {
+                MutexLocker readLocker(m_pendingReadingLock);
+                
+                if (m_iconsPendingReading.contains(icons[i])) {
+                    // Set the new data
+                    icons[i]->setImageData(imageData.get());
+                    
+                    // Remove this icon from the set that needs to be read
+                    m_iconsPendingReading.remove(icons[i]);
+                    
+                    // We have a set of all Page URLs that retain this icon as well as all PageURLs waiting for an icon
+                    // We want to find the intersection of these two sets to notify them
+                    // Check the sizes of these two sets to minimize the number of iterations
+                    const HashSet<String>* outerHash;
+                    const HashSet<String>* innerHash;
+                    
+                    if (icons[i]->retainingPageURLs().size() > m_pageURLsInterestedInIcons.size()) {
+                        outerHash = &m_pageURLsInterestedInIcons;
+                        innerHash = &(icons[i]->retainingPageURLs());
+                    } else {
+                        innerHash = &m_pageURLsInterestedInIcons;
+                        outerHash = &(icons[i]->retainingPageURLs());
+                    }
+                    
+                    HashSet<String>::const_iterator iter = outerHash->begin();
+                    HashSet<String>::const_iterator end = outerHash->end();
+                    for (; iter != end; ++iter) {
+                        if (innerHash->contains(*iter)) {
+                            LOG(IconDatabase, "%s is interesting in the icon we just read.  Adding it to the list and removing it from the interested set", urlForLogging(*iter).utf8().data());
+                            urlsToNotify.add(*iter);
+                        }
+                        
+                        // If we ever get to the point were we've seen every url interested in this icon, break early
+                        if (urlsToNotify.size() == m_pageURLsInterestedInIcons.size())
+                            break;
+                    }
+                    
+                    // We don't need to notify a PageURL twice, so all the ones we're about to notify can be removed from the interested set
+                    if (urlsToNotify.size() == m_pageURLsInterestedInIcons.size())
+                        m_pageURLsInterestedInIcons.clear();
+                    else {
+                        iter = urlsToNotify.begin();
+                        end = urlsToNotify.end();
+                        for (; iter != end; ++iter)
+                            m_pageURLsInterestedInIcons.remove(*iter);
+                    }
+                }
+            }
+        }
+    
+        if (shouldStopThreadActivity())
+            return didAnyWork;
+        
+        // Informal testing shows that draining the autorelease pool every 25 iterations is about as low as we can go
+        // before performance starts to drop off, but we don't want to increase this number because then accumulated memory usage will go up
+        AutodrainedPool pool(25);
+
+        // Now that we don't hold any locks, perform the actual notifications
+        HashSet<String>::iterator iter = urlsToNotify.begin();
+        HashSet<String>::iterator end = urlsToNotify.end();
+        for (unsigned iteration = 0; iter != end; ++iter, ++iteration) {
+            LOG(IconDatabase, "Notifying icon received for pageURL %s", urlForLogging(*iter).utf8().data());
+            m_client->dispatchDidAddIconForPageURL(*iter);
+            if (shouldStopThreadActivity())
+                return didAnyWork;
+            
+            pool.cycle();
+        }
+
+        LOG(IconDatabase, "Done notifying %i pageURLs who just received their icons", urlsToNotify.size());
+        urlsToNotify.clear();
+        
+        if (shouldStopThreadActivity())
+            return didAnyWork;
+    }
+
+    LOG(IconDatabase, "Reading from database took %.4f seconds", currentTime() - timeStamp);
+
+    return didAnyWork;
+}
+
+bool IconDatabase::writeToDatabase()
+{
+    ASSERT_ICON_SYNC_THREAD();
+
+#ifndef NDEBUG
+    double timeStamp = currentTime();
+#endif
+
+    bool didAnyWork = false;
+    
+    // We can copy the current work queue then clear it out - If any new work comes in while we're writing out,
+    // we'll pick it up on the next pass.  This greatly simplifies the locking strategy for this method and remains cohesive with changes
+    // asked for by the database on the main thread
+    Vector<IconSnapshot> iconSnapshots;
+    Vector<PageURLSnapshot> pageSnapshots;
+    {
+        MutexLocker locker(m_pendingSyncLock);
+        
+        iconSnapshots.appendRange(m_iconsPendingSync.begin().values(), m_iconsPendingSync.end().values());
+        m_iconsPendingSync.clear();
+        
+        pageSnapshots.appendRange(m_pageURLsPendingSync.begin().values(), m_pageURLsPendingSync.end().values());
+        m_pageURLsPendingSync.clear();
+    }
+    
+    if (iconSnapshots.size() || pageSnapshots.size())
+        didAnyWork = true;
+        
+    SQLTransaction syncTransaction(m_syncDB);
+    syncTransaction.begin();
+    
+    for (unsigned i = 0; i < iconSnapshots.size(); ++i) {
+        writeIconSnapshotToSQLDatabase(iconSnapshots[i]);
+        LOG(IconDatabase, "Wrote IconRecord for IconURL %s with timeStamp of %li to the DB", urlForLogging(iconSnapshots[i].iconURL).utf8().data(), iconSnapshots[i].timestamp);
+    }
+    
+    for (unsigned i = 0; i < pageSnapshots.size(); ++i) {
+        String iconURL = pageSnapshots[i].iconURL;
+
+        // If the icon URL is empty, this page is meant to be deleted
+        // ASSERTs are sanity checks to make sure the mappings exist if they should and don't if they shouldn't
+        if (pageSnapshots[i].iconURL.isEmpty())
+            removePageURLFromSQLDatabase(pageSnapshots[i].pageURL);
+        else {
+            setIconURLForPageURLInSQLDatabase(pageSnapshots[i].iconURL, pageSnapshots[i].pageURL);
+        }
+        LOG(IconDatabase, "Committed IconURL for PageURL %s to database", urlForLogging(pageSnapshots[i].pageURL).utf8().data());
+    }
+
+    syncTransaction.commit();
+    
+    // Check to make sure there are no dangling PageURLs - If there are, we want to output one log message but not spam the console potentially every few seconds
+    checkForDanglingPageURLs(false);
+
+    LOG(IconDatabase, "Updating the database took %.4f seconds", currentTime() - timeStamp);
 
-int64_t IconDatabase::establishIconIDForIconURL(SQLDatabase& db, const String& iconURL, bool createIfNecessary)
-{    
-    // Get the iconID thats already in this database and return it - or return 0 if we're read-only
-    int64_t iconID = getIconIDForIconURLQuery(db, iconURL);
-    if (iconID || !createIfNecessary)
-        return iconID;
-        
-    // Create the icon table entry for the iconURL
-    return addIconForIconURLQuery(db, iconURL);
+    return didAnyWork;
 }
 
-void IconDatabase::pruneUnretainedIconsOnStartup(Timer<IconDatabase>*)
+void IconDatabase::pruneUnretainedIcons()
 {
-    if (!isOpen())
-        return;
-        
-    // This function should only be called once per run, and ideally only via the timer
-    // on program startup
-    ASSERT(!m_initialPruningComplete);
+    ASSERT_ICON_SYNC_THREAD();
 
-#ifndef NDEBUG
-    double timestamp = currentTime();
-#endif
-    
-    // rdar://4690949 - Need to prune unretained iconURLs here, then prune out all pageURLs that reference
-    // nonexistent icons
-    
-    SQLTransaction pruningTransaction(m_mainDB);
-    pruningTransaction.begin();
+    if (!isOpen())
+        return;        
     
-    // Wipe all PageURLs that aren't retained
-    // Temporary tables in sqlite seem to lose memory permanently so do this by hand instead. This is faster too.
+    // This method should only be called once per run
+    ASSERT(!m_initialPruningComplete);
 
-    Vector<int64_t> pageURLIconIDsToDelete; 
+    // This method relies on having read in all page URLs from the database earlier.
+    ASSERT(m_iconURLImportComplete);
 
     // Get the known PageURLs from the db, and record the ID of any that are not in the retain count set.
-    SQLStatement pageSQL(m_mainDB, "SELECT url, iconID FROM PageURL");
+    Vector<int64_t> pageIDsToDelete; 
+
+    SQLStatement pageSQL(m_syncDB, "SELECT rowid, url FROM PageURL;");
     pageSQL.prepare();
+    
     int result;
     while ((result = pageSQL.step()) == SQLResultRow) {
-        String pageURL = pageSQL.getColumnText16(0);
-        if (pageURL.isEmpty() || !m_pageURLToRetainCount.contains(pageURL))
-            pageURLIconIDsToDelete.append(pageSQL.getColumnInt64(1));
+        MutexLocker locker(m_urlAndIconLock);
+        if (!m_pageURLToRecordMap.contains(pageSQL.getColumnText16(1)))
+            pageIDsToDelete.append(pageSQL.getColumnInt64(0));
     }
-    pageSQL.finalize();
+    
     if (result != SQLResultDone)
         LOG_ERROR("Error reading PageURL table from on-disk DB");
+    pageSQL.finalize();
     
     // Delete page URLs that were in the table, but not in our retain count set.
-    size_t numToDelete = pageURLIconIDsToDelete.size();
+    size_t numToDelete = pageIDsToDelete.size();
     if (numToDelete) {
-        SQLStatement pageDeleteSQL(m_mainDB, "DELETE FROM PageURL WHERE iconID = (?)");
+        SQLTransaction pruningTransaction(m_syncDB);
+        pruningTransaction.begin();
+        
+        SQLStatement pageDeleteSQL(m_syncDB, "DELETE FROM PageURL WHERE rowid = (?);");
         pageDeleteSQL.prepare();
         for (size_t i = 0; i < numToDelete; ++i) {
-            pageDeleteSQL.bindInt64(1, pageURLIconIDsToDelete[i]);
-            if (pageDeleteSQL.step() != SQLResultDone)
-                LOG_ERROR("Unable to delete icon ID %llu from PageURL table", static_cast<unsigned long long>(pageURLIconIDsToDelete[i]));
+            LOG(IconDatabase, "Pruning page with rowid %lli from disk", pageIDsToDelete[i]);
+            pageDeleteSQL.bindInt64(1, pageIDsToDelete[i]);
+            int result = pageDeleteSQL.step();
+            if (result != SQLResultDone)
+                LOG_ERROR("Unabled to delete page with id %lli from disk", pageIDsToDelete[i]);
             pageDeleteSQL.reset();
+            
+            // If the thread was asked to terminate, we should commit what pruning we've done so far, figuring we can
+            // finish the rest later (hopefully)
+            if (shouldStopThreadActivity()) {
+                pruningTransaction.commit();
+                return;
+            }
         }
+        pruningTransaction.commit();
         pageDeleteSQL.finalize();
     }
     
+    // Deleting unreferenced icons from the Icon tables has to be atomic - 
+    // If the user quits while these are taking place, they might have to wait.  Thankfully this will rarely be an issue
+    // A user on a network home directory with a wildly inconsistent database might see quite a pause...
+
+    SQLTransaction pruningTransaction(m_syncDB);
+    pruningTransaction.begin();
+    
     // Wipe Icons that aren't retained
-    if (!m_mainDB.executeCommand("DELETE FROM Icon WHERE Icon.iconID NOT IN (SELECT iconID FROM PageURL);"))
-        LOG_ERROR("Failed to execute SQL to prune unretained icons from the on-disk tables");    
-    
-    // Since we lazily retained the pageURLs without getting the iconURLs or retaining the iconURLs, 
-    // we need to do that now
-    SQLStatement sql(m_mainDB, "SELECT PageURL.url, Icon.url FROM PageURL INNER JOIN Icon ON PageURL.iconID=Icon.iconID");
-    sql.prepare();
-    while((result = sql.step()) == SQLResultRow) {
-        String iconURL = sql.getColumnText16(1);
-        retainIconURL(iconURL);
-        LOG(IconDatabase, "Found a PageURL that mapped to %s", iconURL.ascii().data());
-    }
-    if (result != SQLResultDone)
-        LOG_ERROR("Error reading PageURL->IconURL mappings from on-disk DB");
-    sql.finalize();
+    if (!m_syncDB.executeCommand("DELETE FROM IconData WHERE iconID NOT IN (SELECT iconID FROM PageURL);"))
+        LOG_ERROR("Failed to execute SQL to prune unretained icons from the on-disk IconData table");    
+    if (!m_syncDB.executeCommand("DELETE FROM IconInfo WHERE iconID NOT IN (SELECT iconID FROM PageURL);"))
+        LOG_ERROR("Failed to execute SQL to prune unretained icons from the on-disk IconInfo table");    
     
     pruningTransaction.commit();
-    m_initialPruningComplete = true;
-    
-    // Handle dangling PageURLs, if any
+        
     checkForDanglingPageURLs(true);
 
-#ifndef NDEBUG
-    timestamp = currentTime() - timestamp;
-    if (timestamp <= 1.0)
-        LOG(IconDatabase, "Pruning unretained icons took %.4f seconds", timestamp);
-    else
-        LOG(IconDatabase, "Pruning unretained icons took %.4f seconds - this is much too long!", timestamp);
-
-#endif
-}
-
-void IconDatabase::updateDatabase(Timer<IconDatabase>*)
-{
-    syncDatabase();
-}
-
-void IconDatabase::syncDatabase()
-{
-#ifndef NDEBUG
-    double timestamp = currentTime();
-#endif
-
-    SQLTransaction syncTransaction(*m_currentDB);
-    syncTransaction.begin();
-
-    // First we'll do the pending additions
-    // Starting with the IconDataCaches that need updating/insertion
-    for (HashSet<IconDataCache*>::iterator i = m_iconDataCachesPendingUpdate.begin(), end = m_iconDataCachesPendingUpdate.end(); i != end; ++i) {
-        (*i)->writeToDatabase(*m_currentDB);
-        LOG(IconDatabase, "Wrote IconDataCache for IconURL %s with timestamp of %li to the DB", (*i)->getIconURL().ascii().data(), (*i)->getTimestamp());
-    }
-    m_iconDataCachesPendingUpdate.clear();
-    
-    HashSet<String>::iterator i = m_pageURLsPendingAddition.begin(), end = m_pageURLsPendingAddition.end();
-    for (; i != end; ++i) {
-        setIconURLForPageURLInDatabase(m_pageURLToIconURLMap.get(*i), *i);
-        LOG(IconDatabase, "Committed IconURL for PageURL %s to database", (*i).ascii().data());
-    }
-    m_pageURLsPendingAddition.clear();
-    
-    // Then we'll do the pending deletions
-    // First lets wipe all the pageURLs
-    for (i = m_pageURLsPendingDeletion.begin(), end = m_pageURLsPendingDeletion.end(); i != end; ++i) {    
-        forgetPageURL(*i);
-        LOG(IconDatabase, "Deleted PageURL %s", (*i).ascii().data());
-    }
-    m_pageURLsPendingDeletion.clear();
-
-    // Then get rid of all traces of the icons and IconURLs
-    IconDataCache* icon;    
-    for (i = m_iconURLsPendingDeletion.begin(), end = m_iconURLsPendingDeletion.end(); i != end; ++i) {
-        // Forget the IconDataCache
-        icon = m_iconURLToIconDataCacheMap.get(*i);
-        if (icon)
-            m_iconURLToIconDataCacheMap.remove(*i);
-        delete icon;
-        
-        // Forget the IconURL from the database
-        forgetIconForIconURLFromDatabase(*i);
-        LOG(IconDatabase, "Deleted icon %s", (*i).ascii().data());   
-    }
-    m_iconURLsPendingDeletion.clear();
-    syncTransaction.commit();
-
-    // If the timer was running to cause this update, we can kill the timer as its firing would be redundant
-    m_updateTimer.stop();
-    
-#ifndef NDEBUG
-    timestamp = currentTime() - timestamp;
-    if (timestamp <= 1.0)
-        LOG(IconDatabase, "Updating the database took %.4f seconds", timestamp);
-    else 
-        LOG(IconDatabase, "Updating the database took %.4f seconds - this is much too long!", timestamp);
-    
-    // Check to make sure there are no dangling PageURLs - If there are, we want to output one log message but not spam the console potentially every few seconds
-    checkForDanglingPageURLs(false);
-#endif
+    m_initialPruningComplete = true;
 }
 
 void IconDatabase::checkForDanglingPageURLs(bool pruneIfFound)
 {
+    ASSERT_ICON_SYNC_THREAD();
+    
     // We don't want to keep performing this check and reporting this error if it has already found danglers so we keep track
     static bool danglersFound = false;
     
@@ -904,183 +1583,231 @@ void IconDatabase::checkForDanglingPageURLs(bool pruneIfFound)
     if (pruneIfFound)
         danglersFound = false;
         
-    if (!danglersFound && SQLStatement(*m_currentDB, "SELECT url FROM PageURL WHERE PageURL.iconID NOT IN (SELECT iconID FROM Icon) LIMIT 1;").returnsAtLeastOneResult()) {
+    if (!danglersFound && SQLStatement(m_syncDB, "SELECT url FROM PageURL WHERE PageURL.iconID NOT IN (SELECT iconID FROM IconInfo) LIMIT 1;").returnsAtLeastOneResult()) {
         danglersFound = true;
         LOG_ERROR("Dangling PageURL entries found");
-        if (pruneIfFound && !m_currentDB->executeCommand("DELETE FROM PageURL WHERE iconID NOT IN (SELECT iconID FROM Icon);"))
+        if (pruneIfFound && !m_syncDB.executeCommand("DELETE FROM PageURL WHERE iconID NOT IN (SELECT iconID FROM IconInfo);"))
             LOG_ERROR("Unable to prune dangling PageURLs");
     }
 }
 
-bool IconDatabase::hasEntryForIconURL(const String& iconURL)
+void IconDatabase::removeAllIconsOnThread()
 {
-    if (!isOpen() || iconURL.isEmpty())
-        return false;
-        
-    // First check the in memory mapped icons...
-    if (m_iconURLToIconDataCacheMap.contains(iconURL))
-        return true;
+    ASSERT_ICON_SYNC_THREAD();
 
-    // Then we'll check the main database
-    if (hasIconForIconURLQuery(m_mainDB, iconURL))
-        return true;
-        
-    // Finally, the last resort - check the private browsing database
-    if (m_privateBrowsingEnabled)  
-        if (hasIconForIconURLQuery(m_privateBrowsingDB, iconURL))
-            return true;    
+    LOG(IconDatabase, "Removing all icons on the sync thread");
 
-    // We must not have this iconURL!
-    return false;
+    //Delete all the prepared statements so they can start over
+    deleteAllPreparedStatements();    
+        
+    {
+        MutexLocker locker(m_urlAndIconLock);
+        
+        // Clear all in-memory records of pages and icons
+        m_iconURLToRecordMap.clear();
+        // Deleting the PageURLRecords derefs the IconRecords automagically
+        deleteAllValues(m_pageURLToRecordMap); 
+        m_pageURLToRecordMap.clear();
+        
+        // Clear all in-memory records of things that need to be synced out to disk
+        {
+            MutexLocker locker(m_pendingSyncLock);
+            m_pageURLsPendingSync.clear();
+            m_iconsPendingSync.clear();
+        }
+        
+        // Clear all in-memory records of things that need to be read in from disk
+        {
+            MutexLocker locker(m_pendingReadingLock);
+            m_pageURLsPendingImport.clear();
+            m_pageURLsInterestedInIcons.clear();
+            m_iconsPendingReading.clear();
+            m_loadersPendingDecision.clear();
+        }
+    }
+    
+    // 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_syncDB.clearAllTables();
+    m_syncDB.runVacuumCommand();
+    createDatabaseTables(m_syncDB);
+    
+    LOG(IconDatabase, "Dispatching notification that we removed all icons");
+    m_client->dispatchDidRemoveAllIcons();    
 }
 
-void IconDatabase::setEnabled(bool enabled)
-{
-    if (!enabled && isOpen())
-        close();
-    m_isEnabled = enabled;
+void IconDatabase::deleteAllPreparedStatements()
+{        
+    ASSERT_ICON_SYNC_THREAD();
+    
+    m_setIconIDForPageURLStatement.set(0);
+    m_removePageURLStatement.set(0);
+    m_getIconIDForIconURLStatement.set(0);
+    m_getImageDataForIconURLStatement.set(0);
+    m_addIconToIconInfoStatement.set(0);
+    m_addIconToIconDataStatement.set(0);
+    m_getImageDataStatement.set(0);
+    m_deletePageURLsForIconURLStatement.set(0);
+    m_deleteIconFromIconInfoStatement.set(0);
+    m_deleteIconFromIconDataStatement.set(0);
+    m_updateIconInfoStatement.set(0);
+    m_updateIconDataStatement.set(0);
+    m_setIconInfoStatement.set(0);
+    m_setIconDataStatement.set(0);
 }
 
-bool IconDatabase::enabled() const
+void* IconDatabase::cleanupSyncThread()
 {
-     return m_isEnabled;
+    ASSERT_ICON_SYNC_THREAD();
+    
+#ifndef NDEBUG
+    double timeStamp = currentTime();
+#endif 
+
+    // If the removeIcons flag is set, remove all icons from the db.
+    // Otherwise, do a final write an sync out to disk
+    if (m_removeIconsRequested)
+        removeAllIconsOnThread();
+    else {
+        LOG(IconDatabase, "(THREAD) Doing final writeout and closure of sync thread");
+        writeToDatabase();
+    }
+    
+    // Close the database
+    MutexLocker locker(m_syncLock);
+    
+    m_completeDatabasePath = String();
+    deleteAllPreparedStatements();    
+    m_syncDB.close();
+    
+#ifndef NDEBUG
+    LOG(IconDatabase, "(THREAD) Final closure took %.4f seconds", currentTime() - timeStamp);
+#endif
+    
+    return 0;
 }
 
 bool IconDatabase::imported()
 {
-    if (!m_isImportedSet) {
-        m_imported = importedQuery(m_mainDB);
-        m_isImportedSet = true;
+    ASSERT_ICON_SYNC_THREAD();
+    
+    if (m_isImportedSet)
+        return m_imported;
+        
+    SQLStatement query(m_syncDB, "SELECT IconDatabaseInfo.value FROM IconDatabaseInfo WHERE IconDatabaseInfo.key = \"ImportedSafari2Icons\";");
+    if (query.prepare() != SQLResultOk) {
+        LOG_ERROR("Unable to prepare imported statement");
+        return false;
+    }
+    
+    int result = query.step();
+    if (result == SQLResultRow)
+        result = query.getColumnInt(0);
+    else {
+        if (result != SQLResultDone)
+            LOG_ERROR("imported statement failed");
+        result = 0;
     }
-    return m_imported;
+    
+    m_isImportedSet = true;
+    return m_imported = result;
 }
 
 void IconDatabase::setImported(bool import)
 {
+    ASSERT_ICON_SYNC_THREAD();
+
     m_imported = import;
     m_isImportedSet = true;
-    setImportedQuery(m_mainDB, import);
-}
-
-IconDatabase::~IconDatabase()
-{
-    ASSERT_NOT_REACHED();
+    
+    String queryString = import ?
+        "INSERT INTO IconDatabaseInfo (key, value) VALUES (\"ImportedSafari2Icons\", 1);" :
+        "INSERT INTO IconDatabaseInfo (key, value) VALUES (\"ImportedSafari2Icons\", 0);";
+        
+    SQLStatement query(m_syncDB, queryString);
+    
+    if (query.prepare() != SQLResultOk) {
+        LOG_ERROR("Unable to prepare set imported statement");
+        return;
+    }    
+    
+    if (query.step() != SQLResultDone)
+        LOG_ERROR("set imported statement failed");
 }
 
 // readySQLStatement() handles two things
 // 1 - If the SQLDatabase& argument is different, the statement must be destroyed and remade.  This happens when the user
 //     switches to and from private browsing
 // 2 - Lazy construction of the Statement in the first place, in case we've never made this query before
-inline void readySQLStatement(SQLStatement*& statement, SQLDatabase& db, const String& str)
+inline void readySQLStatement(OwnPtr<SQLStatement>& statement, SQLDatabase& db, const String& str)
 {
     if (statement && (statement->database() != &db || statement->isExpired())) {
         if (statement->isExpired())
-            LOG(IconDatabase, "SQLStatement associated with %s is expired", str.ascii().data());
-        delete statement;
-        statement = 0;
+            LOG(IconDatabase, "SQLStatement associated with %s is expired", str.utf8().data());
+        statement.set(0);
     }
     if (!statement) {
-        statement = new SQLStatement(db, str);
+        statement.set(new SQLStatement(db, str));
         int result;
         result = statement->prepare();
         ASSERT(result == SQLResultOk);
     }
 }
 
-// Any common IconDatabase query should be seperated into a fooQuery() and a *m_fooStatement.  
-// This way we can lazily construct the SQLStatment for a query on its first use, then reuse the Statement binding
-// the new parameter as needed
-// The statement must be deleted in IconDatabase::close() before the actual SQLDatabase::close() call
-// Also, m_fooStatement must be reset() before fooQuery() returns otherwise we will constantly get "database file locked" 
-// errors in various combinations of queries
-
-bool IconDatabase::pageURLTableIsEmptyQuery(SQLDatabase& db)
-{  
-    // We won't make this use a m_fooStatement because its not really a "common" query
-    return !SQLStatement(db, "SELECT iconID FROM PageURL LIMIT 1;").returnsAtLeastOneResult();
-}
-
-PassRefPtr<SharedBuffer> IconDatabase::imageDataForIconURLQuery(SQLDatabase& db, const String& iconURL)
+void IconDatabase::setIconURLForPageURLInSQLDatabase(const String& iconURL, const String& pageURL)
 {
-    RefPtr<SharedBuffer> imageData;
+    ASSERT_ICON_SYNC_THREAD();
     
-    readySQLStatement(m_imageDataForIconURLStatement, db, "SELECT Icon.data FROM Icon WHERE Icon.url = (?);");
-    m_imageDataForIconURLStatement->bindText16(1, iconURL, false);
-    
-    int result = m_imageDataForIconURLStatement->step();
-    if (result == SQLResultRow) {
-        Vector<char> data;
-        m_imageDataForIconURLStatement->getColumnBlobAsVector(0, data);
-        imageData = new SharedBuffer;
-        imageData->append(data.data(), data.size());
-    } else if (result != SQLResultDone)
-        LOG_ERROR("imageDataForIconURLQuery failed");
+    int64_t iconID = getIconIDForIconURLFromSQLDatabase(iconURL);
 
-    m_imageDataForIconURLStatement->reset();
+    if (!iconID)
+        iconID = addIconURLToSQLDatabase(iconURL);
     
-    return imageData.release();
+    if (!iconID) {
+        LOG_ERROR("Failed to establish an ID for iconURL %s", urlForLogging(iconURL).utf8().data());
+        ASSERT(false);
+        return;
+    }
+    
+    setIconIDForPageURLInSQLDatabase(iconID, pageURL);
 }
 
-int IconDatabase::timeStampForIconURLQuery(SQLDatabase& db, const String& iconURL)
+void IconDatabase::setIconIDForPageURLInSQLDatabase(int64_t iconID, const String& pageURL)
 {
-    readySQLStatement(m_timeStampForIconURLStatement, db, "SELECT Icon.stamp FROM Icon WHERE Icon.url = (?);");
-    m_timeStampForIconURLStatement->bindText16(1, iconURL, false);
+    ASSERT_ICON_SYNC_THREAD();
+    
+    readySQLStatement(m_setIconIDForPageURLStatement, m_syncDB, "INSERT INTO PageURL (url, iconID) VALUES ((?), ?);");
+    m_setIconIDForPageURLStatement->bindText16(1, pageURL, false);
+    m_setIconIDForPageURLStatement->bindInt64(2, iconID);
 
-    int result = m_timeStampForIconURLStatement->step();
-    if (result == SQLResultRow)
-        result = m_timeStampForIconURLStatement->getColumnInt(0);
-    else {
-        if (result != SQLResultDone)
-            LOG_ERROR("timeStampForIconURLQuery failed");
-        result = 0;
+    int result = m_setIconIDForPageURLStatement->step();
+    if (result != SQLResultDone) {
+        ASSERT(false);
+        LOG_ERROR("setIconIDForPageURLQuery failed for url %s", urlForLogging(pageURL).utf8().data());
     }
 
-    m_timeStampForIconURLStatement->reset();
-    return result;
+    m_setIconIDForPageURLStatement->reset();
 }
 
-String IconDatabase::iconURLForPageURLQuery(SQLDatabase& db, const String& pageURL)
+void IconDatabase::removePageURLFromSQLDatabase(const String& pageURL)
 {
-    readySQLStatement(m_iconURLForPageURLStatement, db, "SELECT Icon.url FROM Icon, PageURL WHERE PageURL.url = (?) AND Icon.iconID = PageURL.iconID;");
-    m_iconURLForPageURLStatement->bindText16(1, pageURL, false);
+    ASSERT_ICON_SYNC_THREAD();
     
-    int result = m_iconURLForPageURLStatement->step();
-    String iconURL;
-    if (result == SQLResultRow)
-        iconURL = m_iconURLForPageURLStatement->getColumnText16(0);
-    else if (result != SQLResultDone)
-        LOG_ERROR("iconURLForPageURLQuery failed");
-    
-    m_iconURLForPageURLStatement->reset();
-    return iconURL;
-}
-
-void IconDatabase::forgetPageURLQuery(SQLDatabase& db, const String& pageURL)
-{
-    readySQLStatement(m_forgetPageURLStatement, db, "DELETE FROM PageURL WHERE url = (?);");
-    m_forgetPageURLStatement->bindText16(1, pageURL, false);
+    readySQLStatement(m_removePageURLStatement, m_syncDB, "DELETE FROM PageURL WHERE url = (?);");
+    m_removePageURLStatement->bindText16(1, pageURL, false);
 
-    if (m_forgetPageURLStatement->step() != SQLResultDone)
-        LOG_ERROR("forgetPageURLQuery failed");
+    if (m_removePageURLStatement->step() != SQLResultDone)
+        LOG_ERROR("removePageURLFromSQLDatabase failed for url %s", urlForLogging(pageURL).utf8().data());
     
-    m_forgetPageURLStatement->reset();
+    m_removePageURLStatement->reset();
 }
 
-void IconDatabase::setIconIDForPageURLQuery(SQLDatabase& db, int64_t iconID, const String& pageURL)
-{
-    readySQLStatement(m_setIconIDForPageURLStatement, db, "INSERT INTO PageURL (url, iconID) VALUES ((?), ?);");
-    m_setIconIDForPageURLStatement->bindText16(1, pageURL, false);
-    m_setIconIDForPageURLStatement->bindInt64(2, iconID);
-
-    if (m_setIconIDForPageURLStatement->step() != SQLResultDone)
-        LOG_ERROR("setIconIDForPageURLQuery failed");
-
-    m_setIconIDForPageURLStatement->reset();
-}
 
-int64_t IconDatabase::getIconIDForIconURLQuery(SQLDatabase& db, const String& iconURL)
+int64_t IconDatabase::getIconIDForIconURLFromSQLDatabase(const String& iconURL)
 {
-    readySQLStatement(m_getIconIDForIconURLStatement, db, "SELECT Icon.iconID FROM Icon WHERE Icon.url = (?);");
+    ASSERT_ICON_SYNC_THREAD();
+    
+    readySQLStatement(m_getIconIDForIconURLStatement, m_syncDB, "SELECT IconInfo.iconID FROM IconInfo WHERE IconInfo.url = (?);");
     m_getIconIDForIconURLStatement->bindText16(1, iconURL, false);
     
     int64_t result = m_getIconIDForIconURLStatement->step();
@@ -1088,7 +1815,7 @@ int64_t IconDatabase::getIconIDForIconURLQuery(SQLDatabase& db, const String& ic
         result = m_getIconIDForIconURLStatement->getColumnInt64(0);
     else {
         if (result != SQLResultDone)
-            LOG_ERROR("getIconIDForIconURLQuery failed");
+            LOG_ERROR("getIconIDForIconURLFromSQLDatabase failed for url %s", urlForLogging(iconURL).utf8().data());
         result = 0;
     }
 
@@ -1096,119 +1823,171 @@ int64_t IconDatabase::getIconIDForIconURLQuery(SQLDatabase& db, const String& ic
     return result;
 }
 
-int64_t IconDatabase::addIconForIconURLQuery(SQLDatabase& db, const String& iconURL)
+int64_t IconDatabase::addIconURLToSQLDatabase(const String& iconURL)
 {
-    readySQLStatement(m_addIconForIconURLStatement, db, "INSERT INTO Icon (url) VALUES ((?));");
-    m_addIconForIconURLStatement->bindText16(1, iconURL, false);
+    ASSERT_ICON_SYNC_THREAD();
     
-    int64_t result = m_addIconForIconURLStatement->step();
-    if (result == SQLResultDone)
-        result = db.lastInsertRowID();
-    else {
-        LOG_ERROR("addIconForIconURLQuery failed");
-        result = 0;
+    // There would be a transaction here to make sure these two inserts are atomic
+    // In practice the only caller of this method is always wrapped in a transaction itself so placing another
+    // here is unnecessary
+    
+    readySQLStatement(m_addIconToIconInfoStatement, m_syncDB, "INSERT INTO IconInfo (url, stamp) VALUES (?, 0);");
+    m_addIconToIconInfoStatement->bindText16(1, iconURL);
+    
+    int result = m_addIconToIconInfoStatement->step();
+    m_addIconToIconInfoStatement->reset();
+    if (result != SQLResultDone) {
+        LOG_ERROR("addIconURLToSQLDatabase failed to insert %s into IconInfo", urlForLogging(iconURL).utf8().data());
+        return 0;
     }
-
-    m_addIconForIconURLStatement->reset();
-    return result;
+    int64_t iconID = m_syncDB.lastInsertRowID();
+    
+    readySQLStatement(m_addIconToIconDataStatement, m_syncDB, "INSERT INTO IconData (iconID, data) VALUES (?, ?);");
+    m_addIconToIconDataStatement->bindInt64(1, iconID);
+    
+    result = m_addIconToIconDataStatement->step();
+    m_addIconToIconDataStatement->reset();
+    if (result != SQLResultDone) {
+        LOG_ERROR("addIconURLToSQLDatabase failed to insert %s into IconData", urlForLogging(iconURL).utf8().data());
+        return 0;
+    }
+    
+    return iconID;
 }
 
-bool IconDatabase::hasIconForIconURLQuery(SQLDatabase& db, const String& iconURL)
+PassRefPtr<SharedBuffer> IconDatabase::getImageDataForIconURLFromSQLDatabase(const String& iconURL)
 {
-    readySQLStatement(m_hasIconForIconURLStatement, db, "SELECT Icon.iconID FROM Icon WHERE Icon.url = (?);");
-    m_hasIconForIconURLStatement->bindText16(1, iconURL, false);
-
-    int result = m_hasIconForIconURLStatement->step();
-
-    if (result != SQLResultRow && result != SQLResultDone)
-        LOG_ERROR("hasIconForIconURLQuery failed");
+    ASSERT_ICON_SYNC_THREAD();
+    
+    RefPtr<SharedBuffer> imageData;
+    
+    readySQLStatement(m_getImageDataForIconURLStatement, m_syncDB, "SELECT IconData.data FROM IconData WHERE IconData.iconID IN (SELECT iconID FROM IconInfo WHERE IconInfo.url = (?));");
+    m_getImageDataForIconURLStatement->bindText16(1, iconURL, false);
+    
+    int result = m_getImageDataForIconURLStatement->step();
+    if (result == SQLResultRow) {
+        Vector<char> data;
+        m_getImageDataForIconURLStatement->getColumnBlobAsVector(0, data);
+        imageData = new SharedBuffer;
+        imageData->append(data.data(), data.size());
+    } else if (result != SQLResultDone)
+        LOG_ERROR("getImageDataForIconURLFromSQLDatabase failed for url %s", urlForLogging(iconURL).utf8().data());
 
-    m_hasIconForIconURLStatement->reset();
-    return result == SQLResultRow;
+    m_getImageDataForIconURLStatement->reset();
+    
+    return imageData.release();
 }
 
-bool IconDatabase::importedQuery(SQLDatabase& db)
+void IconDatabase::removeIconFromSQLDatabase(const String& iconURL)
 {
-    readySQLStatement(m_importedStatement, db, "SELECT IconDatabaseInfo.value FROM IconDatabaseInfo WHERE IconDatabaseInfo.key = \"ImportedSafari2Icons\";");
+    ASSERT_ICON_SYNC_THREAD();
     
-    int result = m_importedStatement->step();
+    if (iconURL.isEmpty())
+        return;
 
-    if (result == SQLResultRow)
-        result = m_importedStatement->getColumnInt(0);
-    else {
-        if (result != SQLResultDone)
-            LOG_ERROR("importedQuery failed");
-        result = 0;
+    // There would be a transaction here to make sure these removals are atomic
+    // In practice the only caller of this method is always wrapped in a transaction itself so placing another here is unnecessary
+    
+    int64_t iconID = getIconIDForIconURLFromSQLDatabase(iconURL);
+    ASSERT(iconID);
+    if (!iconID) {
+        LOG_ERROR("Unable to get iconID for icon URL %s to delete it from the database", urlForLogging(iconURL).utf8().data());
+        return;
     }
-
-    m_importedStatement->reset();
-    return result;
-}
-
-void IconDatabase::setImportedQuery(SQLDatabase& db, bool imported)
-{
-    if (imported)
-        readySQLStatement(m_setImportedStatement, db, "INSERT INTO IconDatabaseInfo (key, value) VALUES (\"ImportedSafari2Icons\", 1);");
-    else
-        readySQLStatement(m_setImportedStatement, db, "INSERT INTO IconDatabaseInfo (key, value) VALUES (\"ImportedSafari2Icons\", 0);");
-
-    int result = m_setImportedStatement->step();
-
-    if (result != SQLResultDone)
-        LOG_ERROR("setImportedQuery failed");
-
-    m_setImportedStatement->reset();
+    
+    readySQLStatement(m_deletePageURLsForIconURLStatement, m_syncDB, "DELETE FROM PageURL WHERE PageURL.iconID = (?);");
+    m_deletePageURLsForIconURLStatement->bindInt64(1, iconID);
+    
+    if (m_deletePageURLsForIconURLStatement->step() != SQLResultDone)
+        LOG_ERROR("m_deletePageURLsForIconURLStatement failed for url %s", urlForLogging(iconURL).utf8().data());
+    
+    readySQLStatement(m_deleteIconFromIconInfoStatement, m_syncDB, "DELETE FROM IconInfo WHERE IconInfo.iconID = (?);");
+    m_deleteIconFromIconInfoStatement->bindInt64(1, iconID);
+    
+    if (m_deleteIconFromIconInfoStatement->step() != SQLResultDone)
+        LOG_ERROR("m_deleteIconFromIconInfoStatement failed for url %s", urlForLogging(iconURL).utf8().data());
+        
+    readySQLStatement(m_deleteIconFromIconDataStatement, m_syncDB, "DELETE FROM IconData WHERE IconData.iconID = (?);");
+    m_deleteIconFromIconDataStatement->bindInt64(1, iconID);
+    
+    if (m_deleteIconFromIconDataStatement->step() != SQLResultDone)
+        LOG_ERROR("m_deleteIconFromIconDataStatement failed for url %s", urlForLogging(iconURL).utf8().data());
+        
+    m_deletePageURLsForIconURLStatement->reset();
+    m_deleteIconFromIconInfoStatement->reset();
+    m_deleteIconFromIconDataStatement->reset();
 }
 
-bool IconDatabase::checkIntegrity()
+void IconDatabase::writeIconSnapshotToSQLDatabase(const IconSnapshot& snapshot)
 {
-    SQLStatement integrity(m_mainDB, "PRAGMA integrity_check;");
-    if (integrity.prepare() != SQLResultOk) {
-        LOG_ERROR("checkIntegrity failed to execute");
-        return false;
-    }
+    ASSERT_ICON_SYNC_THREAD();
     
-    int resultCode = integrity.step();
-    if (resultCode == SQLResultOk)
-        return true;
+    if (snapshot.iconURL.isEmpty())
+        return;
         
-    if (resultCode != SQLResultRow)
-        return false;
-
-    int columns = integrity.columnCount();
-    if (columns != 1) {
-        LOG_ERROR("Received %i columns performing integrity check, should be 1", columns);
-        return false;
+    // A nulled out timestamp and data means this icon is destined to be deleted - do that instead of writing it out
+    if (!snapshot.timestamp && !snapshot.data) {
+        LOG(IconDatabase, "Removing %s from on-disk database", urlForLogging(snapshot.iconURL).utf8().data());
+        removeIconFromSQLDatabase(snapshot.iconURL);
+        return;
     }
+
+    // There would be a transaction here to make sure these removals are atomic
+    // In practice the only caller of this method is always wrapped in a transaction itself so placing another here is unnecessary
         
-    String resultText = integrity.getColumnText16(0);
+    // Get the iconID for this url
+    int64_t iconID = getIconIDForIconURLFromSQLDatabase(snapshot.iconURL);
+    
+    // If there is already an iconID in place, update the database.  
+    // Otherwise, insert new records
+    if (iconID) {    
+        readySQLStatement(m_updateIconInfoStatement, m_syncDB, "UPDATE IconInfo SET stamp = ?, url = ? WHERE iconID = ?;");
+        m_updateIconInfoStatement->bindInt64(1, snapshot.timestamp);
+        m_updateIconInfoStatement->bindText16(2, snapshot.iconURL);
+        m_updateIconInfoStatement->bindInt64(3, iconID);
+
+        if (m_updateIconInfoStatement->step() != SQLResultDone)
+            LOG_ERROR("Failed to update icon info for url %s", urlForLogging(snapshot.iconURL).utf8().data());
         
-    // A successful, no-error integrity check will be "ok" - all other strings imply failure
-    if (resultText == "ok")
-        return true;
-    
-    LOG_ERROR("Icon database integrity check failed - \n%s", resultText.ascii().data());
-    return false;
-}
+        m_updateIconInfoStatement->reset();
+        
+        readySQLStatement(m_updateIconDataStatement, m_syncDB, "UPDATE IconData SET data = ? WHERE iconID = ?;");
+        m_updateIconDataStatement->bindInt64(2, iconID);
+                
+        // If we *have* image data, bind it to this statement - Otherwise the DB will get "null" for the blob data, 
+        // signifying that this icon doesn't have any data    
+        if (snapshot.data && snapshot.data->size())
+            m_updateIconDataStatement->bindBlob(1, snapshot.data->data(), snapshot.data->size());
+        
+        if (m_updateIconDataStatement->step() != SQLResultDone)
+            LOG_ERROR("Failed to update icon data for url %s", urlForLogging(snapshot.iconURL).utf8().data());
 
-size_t IconDatabase::pageURLMappingCount() const
-{
-    return 0;
-}
+        m_updateIconDataStatement->reset();
+    } else {    
+        readySQLStatement(m_setIconInfoStatement, m_syncDB, "INSERT INTO IconInfo (url,stamp) VALUES (?, ?);");
+        m_setIconInfoStatement->bindText16(1, snapshot.iconURL);
+        m_setIconInfoStatement->bindInt64(2, snapshot.timestamp);
 
-size_t IconDatabase::retainedPageURLCount() const
-{
-    return 0;
-}
+        if (m_setIconInfoStatement->step() != SQLResultDone)
+            LOG_ERROR("Failed to set icon info for url %s", urlForLogging(snapshot.iconURL).utf8().data());
+        
+        m_setIconInfoStatement->reset();
+        
+        int64_t iconID = m_syncDB.lastInsertRowID();
 
-size_t IconDatabase::iconRecordCount() const
-{
-    return 0;
-}
+        readySQLStatement(m_setIconDataStatement, m_syncDB, "INSERT INTO IconData (iconID, data) VALUES (?, ?);");
+        m_setIconDataStatement->bindInt64(1, iconID);
 
-size_t IconDatabase::iconRecordCountWithData() const
-{
-    return 0;
+        // If we *have* image data, bind it to this statement - Otherwise the DB will get "null" for the blob data, 
+        // signifying that this icon doesn't have any data    
+        if (snapshot.data && snapshot.data->size())
+            m_setIconDataStatement->bindBlob(2, snapshot.data->data(), snapshot.data->size());
+        
+        if (m_setIconDataStatement->step() != SQLResultDone)
+            LOG_ERROR("Failed to set icon data for url %s", urlForLogging(snapshot.iconURL).utf8().data());
+
+        m_setIconDataStatement->reset();
+    }
 }
 
 } // namespace WebCore
index ac3cb1e7aea3a42e1abe500e3130e6bc90054085..71d10e4b765938032045089e6ebf6143fe89108f 100644 (file)
 #endif
 
 #include "StringHash.h"
+#include "Threading.h"
 #include "Timer.h"
-#include <wtf/Noncopyable.h>
-#include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
 
 namespace WebCore { 
 
+class DocumentLoader;
 class Image;
 class IntSize;
-class IconDataCache;
+class IconDatabaseClient;
+class IconRecord;
+class IconSnapshot;
+class KURL;
+class PageURLRecord;
+class PageURLSnapshot;
 class SharedBuffer;
 
 #if ENABLE(ICONDATABASE)
 class SQLTransaction;
 #endif
 
+enum IconLoadDecision {
+    IconLoadYes,
+    IconLoadNo,
+    IconLoadUnknown
+};
+
 class IconDatabase : Noncopyable {
+
+// *** Main Thread Only ***
 public:
+    void setClient(IconDatabaseClient*);
+
     bool open(const String& path);
-    bool isOpen() const;
     void close();
-    
-    String databasePath() const;
-    
+            
     void removeAllIcons();
-    
-    bool isEmpty();
+
     Image* iconForPageURL(const String&, const IntSize&, bool cache = true);
-    Image* iconForIconURL(const String&, const IntSize&, bool cache = true);
+    void readIconForPageURLFromDisk(const String&);
     String iconURLForPageURL(const String&);
     Image* defaultIcon(const IntSize&);
 
     void retainIconForPageURL(const String&);
     void releaseIconForPageURL(const String&);
-    
-    void setPrivateBrowsingEnabled(bool flag);
-    bool isPrivateBrowsingEnabled() const;
-
-    bool hasEntryForIconURL(const String&);
 
-    bool isIconExpiredForIconURL(const String&);
-    
     void setIconDataForIconURL(PassRefPtr<SharedBuffer> data, const String&);
-    void setHaveNoIconForIconURL(const String&);
-    
-    // Returns true if the set actually took place, false if the mapping already existed
-    bool setIconURLForPageURL(const String& iconURL, const String& pageURL);
+    void setIconURLForPageURL(const String& iconURL, const String& pageURL);
+
+    IconLoadDecision loadDecisionForIconURL(const String&, DocumentLoader*);
+    bool iconDataKnownForIconURL(const String&);
     
     void setEnabled(bool enabled);
-    bool enabled() const;
-
-    bool imported();
-    void setImported(bool);
-
-    static const String& defaultDatabaseFilename();
+    bool isEnabled() const;
     
-    // For clients to tell the IconDatabase that when it does open it should run the integrity check.
-    // The flag is reset to false after the integrity check is run
+    void setPrivateBrowsingEnabled(bool flag);
+    bool isPrivateBrowsingEnabled() const;
+    
+    static void delayDatabaseCleanup();
+    static void allowDatabaseCleanup();
     static void checkIntegrityBeforeOpening();
         
-    bool checkIntegrity();
-    
     // Support for WebCoreStatistics in WebKit
-    size_t pageURLMappingCount() const;
-    size_t retainedPageURLCount() const;
-    size_t iconRecordCount() const;
-    size_t iconRecordCountWithData() const;
-    
+    size_t pageURLMappingCount();
+    size_t retainedPageURLCount();
+    size_t iconRecordCount();
+    size_t iconRecordCountWithData();
+
 private:
     IconDatabase();
     ~IconDatabase();
     friend IconDatabase* iconDatabase();
 
 #if ENABLE(ICONDATABASE)
-    // This tries to get the iconID for the IconURL and, if it doesn't exist and createIfNecessary is true,
-    // it will create the entry and return the new iconID
-    int64_t establishIconIDForIconURL(SQLDatabase&, const String&, bool createIfNecessary = true);
+    // This is called on the main thread via the callOnMainThread() function which currently
+    // doesn't have any way to allow it to be an instance method, which it should be
+    static void notifyPendingLoadDecisions();
     
-    // This method returns the SiteIcon for the given IconURL and, if it doesn't exist it creates it first
-    IconDataCache* getOrCreateIconDataCache(const String& iconURL);
+    void notifyPendingLoadDecisionsInternal();
 
-    // Remove traces of the given pageURL
-    void forgetPageURL(const String& pageURL);
+    void wakeSyncThread();
+    void scheduleOrDeferSyncTimer();
+    Timer<IconDatabase> m_syncTimer;
+    void syncTimerFired(Timer<IconDatabase>*);
     
-    // Remove the current database entry for this IconURL
-    void forgetIconForIconURLFromDatabase(const String&);
-    
-    void setIconURLForPageURLInDatabase(const String&, const String&);
-        
-    // 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>*);
+    pthread_t m_syncThread;
+    bool m_syncThreadRunning;
     
-    // Called by the prune timer, this method periodically removes all the icons in the pending-deletion
-    // queue
-    void updateDatabase(Timer<IconDatabase>*);
-    
-    // This is called by updateDatabase and when private browsing shifts, and when the DB is closed down
-    void syncDatabase();
+    HashSet<RefPtr<DocumentLoader> > m_loadersPendingDecision;
 
-    // Called to eliminate database inconsistency where pages point to non-existent iconIDs
-    void checkForDanglingPageURLs(bool pruneIfFound);
-    
-    // Determine if an IconURL is still retained by anyone
-    bool isIconURLRetained(const String&);
-    
-    // Do a quick check to make sure the database tables are in place and the db version is current
-    bool isValidDatabase(SQLDatabase&);
-        
-    // Create the tables and triggers for the given database.
-    void createDatabaseTables(SQLDatabase&);
-    
-    // Returns the image data for the given IconURL, checking both databases if necessary
-    PassRefPtr<SharedBuffer> imageDataForIconURL(const String& iconURL);
-    
-    // Retains an iconURL, bringing it back from the brink if it was pending deletion
-    void retainIconURL(const String& iconURL);
-    
-    // Releases an iconURL, putting it on the pending delete queue if it's totally released
-    void releaseIconURL(const String& iconURL);
-    
-    // Query - Checks for at least 1 entry in the PageURL table
-    bool pageURLTableIsEmptyQuery(SQLDatabase&);
-    
-    // Query - Returns the time stamp for an Icon entry
-    int timeStampForIconURLQuery(SQLDatabase&, const String& iconURL);    
-    SQLStatement* m_timeStampForIconURLStatement;
-    
-    // Query - Returns the IconURL for a PageURL
-    String iconURLForPageURLQuery(SQLDatabase&, const String& pageURL);    
-    SQLStatement* m_iconURLForPageURLStatement;
-    
-    // Query - Checks for the existence of the given IconURL in the Icon table
-    bool hasIconForIconURLQuery(SQLDatabase& db, const String& iconURL);
-    SQLStatement* m_hasIconForIconURLStatement;
-    
-    // Query - Deletes a PageURL from the PageURL table
-    void forgetPageURLQuery(SQLDatabase& db, const String& pageURL);
-    SQLStatement* m_forgetPageURLStatement;
+    IconRecord* m_defaultIconRecord;
+#endif // ENABLE(ICONDATABASE)
+
+// *** Any Thread ***
+public:
+    bool isOpen() const;
+    String databasePath() const;
+    static String defaultDatabaseFilename();
+
+private:
+#if ENABLE(ICONDATABASE)
+    IconRecord* getOrCreateIconRecord(const String& iconURL);
+    PageURLRecord* getOrCreatePageURLRecord(const String& pageURL);
     
-    // Query - Sets the Icon.iconID for a PageURL in the PageURL table
-    void setIconIDForPageURLQuery(SQLDatabase& db, int64_t, const String&);
-    SQLStatement* m_setIconIDForPageURLStatement;
+    bool m_isEnabled;
+    bool m_privateBrowsingEnabled;
     
-    // Query - Returns the iconID for the given IconURL
-    int64_t getIconIDForIconURLQuery(SQLDatabase& db, const String& iconURL);
-    SQLStatement* m_getIconIDForIconURLStatement;
+    mutable Mutex m_syncLock;
+    ThreadCondition m_syncCondition;
+    // Holding m_syncLock is required when accessing m_completeDatabasePath
+    String m_completeDatabasePath;
+
+    Mutex m_removeLock;
+    ThreadCondition m_removeCondition;
     
-    // Query - Creates the Icon entry for the given IconURL and returns the resulting iconID
-    int64_t addIconForIconURLQuery(SQLDatabase& db, const String& iconURL);
-    SQLStatement* m_addIconForIconURLStatement;
+    bool m_threadTerminationRequested;
+    bool m_removeIconsRequested;
+    bool m_iconURLImportComplete;
     
-    // Query - Returns the image data from the given database for the given IconURL
-    PassRefPtr<SharedBuffer> imageDataForIconURLQuery(SQLDatabase& db, const String& iconURL);
-    SQLStatement* m_imageDataForIconURLStatement;
+    Mutex m_urlAndIconLock;
+    // Holding m_urlAndIconLock is required when accessing any of the following data structures or the objects they contain
+    HashMap<String, IconRecord*> m_iconURLToRecordMap;
+    HashMap<String, PageURLRecord*> m_pageURLToRecordMap;
+    HashSet<String> m_retainedPageURLs;
 
-    // Query - Returns whether or not the "imported" flag is set
-    bool importedQuery(SQLDatabase&);
-    SQLStatement* m_importedStatement;
+    Mutex m_pendingSyncLock;
+    // Holding m_pendingSyncLock is required when accessing any of the following data structures
+    HashMap<String, PageURLSnapshot> m_pageURLsPendingSync;
+    HashMap<String, IconSnapshot> m_iconsPendingSync;
     
-    // Query - Sets the "imported" flag
-    void setImportedQuery(SQLDatabase&, bool);
-    SQLStatement* m_setImportedStatement;
-
-    void deleteAllPreparedStatements(bool withSync);
+    Mutex m_pendingReadingLock;    
+    // Holding m_pendingSyncLock is required when accessing any of the following data structures - when dealing with IconRecord*s, holding m_urlAndIconLock is also required
+    HashSet<String> m_pageURLsPendingImport;
+    HashSet<String> m_pageURLsInterestedInIcons;
+    HashSet<IconRecord*> m_iconsPendingReading;
 
-    SQLDatabase m_mainDB;
-    SQLDatabase m_privateBrowsingDB;
-    SQLDatabase* m_currentDB;
-    
-    IconDataCache* m_defaultIconDataCache;
+// *** Sync Thread Only ***
+public:
+    // Should be used only on the sync thread and only by the Safari 2 Icons import procedure
+    void importIconURLForPageURL(const String& iconURL, const String& pageURL);
+    void importIconDataForIconURL(PassRefPtr<SharedBuffer> data, const String& iconURL);
     
-    bool m_isEnabled;
-    bool m_privateBrowsingEnabled;
+    bool shouldStopThreadActivity() const;
+
+private:    
+    static void* iconDatabaseSyncThreadStart(void *);
+    void* iconDatabaseSyncThread();
     
-    Timer<IconDatabase> m_startupTimer;
-    Timer<IconDatabase> m_updateTimer;
+    // The following block of methods are called exclusively by the sync thread to manage i/o to and from the database
+    // Each method should periodically monitor m_threadTerminationRequested when it makes sense to return early on shutdown
+    void performOpenInitialization();
+    bool checkIntegrity();
+    void performURLImport();
+    void* syncThreadMainLoop();
+    bool readFromDatabase();
+    bool writeToDatabase();
+    void pruneUnretainedIcons();
+    void checkForDanglingPageURLs(bool pruneIfFound);
+    void removeAllIconsOnThread();
+    void deleteAllPreparedStatements();
+    void* cleanupSyncThread();
+
+    // Record (on disk) whether or not Safari 2-style icons were imported (once per dataabse)
+    bool imported();
+    void setImported(bool);
     
     bool m_initialPruningComplete;
+        
+    void setIconURLForPageURLInSQLDatabase(const String&, const String&);
+    void setIconIDForPageURLInSQLDatabase(int64_t, const String&);
+    void removePageURLFromSQLDatabase(const String& pageURL);
+    int64_t getIconIDForIconURLFromSQLDatabase(const String& iconURL);
+    int64_t addIconURLToSQLDatabase(const String&);
+    PassRefPtr<SharedBuffer> getImageDataForIconURLFromSQLDatabase(const String& iconURL);
+    void removeIconFromSQLDatabase(const String& iconURL);
+    void writeIconSnapshotToSQLDatabase(const IconSnapshot&);    
     
-    bool m_imported;
-    mutable bool m_isImportedSet;
+    // The client is set by the main thread before the thread starts, and from then on is only used by the sync thread
+    IconDatabaseClient* m_client;
     
-    HashMap<String, IconDataCache*> m_iconURLToIconDataCacheMap;
-    HashSet<IconDataCache*> m_iconDataCachesPendingUpdate;
+    SQLDatabase m_syncDB;
     
-    HashMap<String, String> m_pageURLToIconURLMap;
-    HashSet<String> m_pageURLsPendingAddition;
-
-    // This will keep track of the retaincount for each pageURL
-    HashMap<String, int> m_pageURLToRetainCount;
-    // This will keep track of the retaincount for each iconURL (ie - the number of pageURLs retaining this icon)
-    HashMap<String, int> m_iconURLToRetainCount;
-
-    HashSet<String> m_pageURLsPendingDeletion;
-    HashSet<String> m_iconURLsPendingDeletion;
-#endif
+    // Track whether the "Safari 2" import is complete and/or set in the database
+    bool m_imported;
+    bool m_isImportedSet;
+    
+    OwnPtr<SQLStatement> m_setIconIDForPageURLStatement;
+    OwnPtr<SQLStatement> m_removePageURLStatement;
+    OwnPtr<SQLStatement> m_getIconIDForIconURLStatement;
+    OwnPtr<SQLStatement> m_getImageDataForIconURLStatement;
+    OwnPtr<SQLStatement> m_addIconToIconInfoStatement;
+    OwnPtr<SQLStatement> m_addIconToIconDataStatement;
+    OwnPtr<SQLStatement> m_getImageDataStatement;
+    OwnPtr<SQLStatement> m_deletePageURLsForIconURLStatement;
+    OwnPtr<SQLStatement> m_deleteIconFromIconInfoStatement;
+    OwnPtr<SQLStatement> m_deleteIconFromIconDataStatement;
+    OwnPtr<SQLStatement> m_updateIconInfoStatement;
+    OwnPtr<SQLStatement> m_updateIconDataStatement;
+    OwnPtr<SQLStatement> m_setIconInfoStatement;
+    OwnPtr<SQLStatement> m_setIconDataStatement;
+#endif // ENABLE(ICONDATABASE)
 };
 
 // Function to obtain the global icon database.
diff --git a/WebCore/loader/icon/IconDatabaseClient.h b/WebCore/loader/icon/IconDatabaseClient.h
new file mode 100644 (file)
index 0000000..e642895
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef IconDatabaseClient_h
+#define IconDatabaseClient_h
+
+// All of these client methods will be called from a non-main thread
+// Take appropriate measures
+namespace WebCore {
+
+class String;
+
+class IconDatabaseClient {
+public:
+    virtual ~IconDatabaseClient() { }
+    virtual bool performImport() { return true; }
+    virtual void dispatchDidRemoveAllIcons() { }
+    virtual void dispatchDidAddIconForPageURL(const String& pageURL) { }
+};
+} // namespace WebCore 
+#endif
index 177474beddd94600e69367d4e5c5bfef383d0f6a..bef24f3606fbd8704e74990cc8c65dcd868e94a7 100644 (file)
@@ -45,7 +45,7 @@ const int missingIconExpirationTime = 60*60*24*7;
 
 const int updateTimerDelay = 5; 
 
-const String& IconDatabase::defaultDatabaseFilename()
+String IconDatabase::defaultDatabaseFilename()
 {
     static String defaultDatabaseFilename = "Icons.db";
     return defaultDatabaseFilename;
@@ -85,11 +85,6 @@ void IconDatabase::removeAllIcons()
 {
 }
 
-bool IconDatabase::isEmpty()
-{
-  return true;
-}
-
 void IconDatabase::setPrivateBrowsingEnabled(bool flag)
 {
 }
@@ -99,12 +94,23 @@ bool IconDatabase::isPrivateBrowsingEnabled() const
     return false;
 }
 
+void IconDatabase::readIconForPageURLFromDisk(const String&)
+{
+
+}
+
 Image* IconDatabase::iconForPageURL(const String& pageURL, const IntSize& size, bool cache)
 {
     return defaultIcon(size);
 }
 
-bool IconDatabase::isIconExpiredForIconURL(const String& iconURL)
+
+IconLoadDecision IconDatabase::loadDecisionForIconURL(const String&, DocumentLoader*)
+{
+    return IconLoadNo;
+}
+
+bool IconDatabase::iconDataKnownForIconURL(const String&)
 {
     return false;
 }
@@ -131,25 +137,15 @@ void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> data, const St
 {
 }
 
-void IconDatabase::setHaveNoIconForIconURL(const String& iconURL)
-{
-}
-
-bool IconDatabase::setIconURLForPageURL(const String& iconURL, const String& pageURL)
+void IconDatabase::setIconURLForPageURL(const String& iconURL, const String& pageURL)
 {
-    return false;
-}
-
-bool IconDatabase::hasEntryForIconURL(const String& iconURL)
-{
-    return false;
 }
 
 void IconDatabase::setEnabled(bool enabled)
 {
 }
 
-bool IconDatabase::enabled() const
+bool IconDatabase::isEnabled() const
 {
     return false;
 }
@@ -177,4 +173,16 @@ void IconDatabase::checkIntegrityBeforeOpening()
 {
 }
 
+void IconDatabase::delayDatabaseCleanup()
+{
+}
+
+void IconDatabase::allowDatabaseCleanup()
+{
+}
+
+void IconDatabase::setClient(IconDatabaseClient*)
+{
+}
+
 } // namespace WebCore
diff --git a/WebCore/loader/icon/IconRecord.cpp b/WebCore/loader/icon/IconRecord.cpp
new file mode 100644 (file)
index 0000000..98bdba7
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "IconRecord.h"
+
+#include "BitmapImage.h"
+#include "IconDatabase.h"
+#include "Logging.h"
+#include "SQLStatement.h"
+#include "SQLTransaction.h"
+#include "SystemTime.h"
+
+#include <limits.h>
+
+namespace WebCore {
+
+IconRecord::IconRecord(const String& url)
+    : m_iconURL(url)
+    , m_stamp(0)
+    , m_dataSet(false)
+{
+
+}
+
+IconRecord::~IconRecord()
+{
+    LOG(IconDatabase, "Destroying IconRecord for icon url %s", m_iconURL.ascii().data());
+}
+
+Image* IconRecord::image(const IntSize& size)
+{
+    // FIXME rdar://4680377 - For size right now, we are returning our one and only image and the Bridge
+    // is resizing it in place.  We need to actually store all the original representations here and return a native
+    // one, or resize the best one to the requested size and cache that result.
+    
+    return m_image.get();
+}
+
+void IconRecord::setImageData(PassRefPtr<SharedBuffer> data)
+{
+    // It's okay to delete the raw image here. Any existing clients using this icon will be
+    // managing an image that was created with a copy of this raw image data.
+    m_image.set(new BitmapImage());
+
+    // Copy the provided data into the buffer of the new Image object.
+    if (!m_image->setData(data, true)) {
+        LOG(IconDatabase, "Manual image data for iconURL '%s' FAILED - it was probably invalid image data", m_iconURL.ascii().data());
+        m_image.set(0);
+    }
+    
+    m_dataSet = true;
+}
+
+void IconRecord::loadImageFromResource(const char* resource)
+{
+    if (!resource)
+        return;
+        
+    m_image.set(Image::loadPlatformResource(resource));
+    m_dataSet = true;
+}
+
+ImageDataStatus IconRecord::imageDataStatus()
+{
+    if (!m_dataSet)
+        return ImageDataStatusUnknown;
+    if (!m_image)
+        return ImageDataStatusMissing;
+    return ImageDataStatusPresent;
+}
+
+IconSnapshot IconRecord::snapshot(bool forDeletion) const
+{
+    if (forDeletion)
+        return IconSnapshot(m_iconURL, 0, 0);
+    
+    return IconSnapshot(m_iconURL, m_stamp, m_image ? m_image->data() : 0);
+}
+
+} // namespace WebCore    
diff --git a/WebCore/loader/icon/IconRecord.h b/WebCore/loader/icon/IconRecord.h
new file mode 100644 (file)
index 0000000..d2b8912
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef IconRecord_h
+#define IconRecord_h
+
+#include "PageURLRecord.h"
+#include "Shared.h"
+#include "SharedBuffer.h"
+
+#include <wtf/HashSet.h>
+#include <wtf/OwnPtr.h>
+#include "PlatformString.h"
+#include "StringHash.h"
+
+namespace WebCore { 
+
+class IconDataSnapshot;
+class Image;
+class IntSize;
+class SQLDatabase;
+
+enum ImageDataStatus {
+    ImageDataStatusPresent, ImageDataStatusMissing, ImageDataStatusUnknown
+};
+
+class IconSnapshot {
+public:
+    IconSnapshot() : timestamp(0) { }
+    
+    IconSnapshot(const String& url, int stamp, SharedBuffer* theData)
+        : iconURL(url)
+        , timestamp(stamp)
+        , data(theData)
+    { }
+        
+    String iconURL;
+    int timestamp;
+    RefPtr<SharedBuffer> data;
+};
+    
+class IconRecord : public Shared<IconRecord> {
+    friend class PageURLRecord;
+public:
+    IconRecord(const String& url); 
+    ~IconRecord();
+    
+    time_t getTimestamp() { return m_stamp; }
+    void setTimestamp(time_t stamp) { m_stamp = stamp; }
+        
+    void setImageData(PassRefPtr<SharedBuffer> data);
+    Image* image(const IntSize&);    
+    
+    String iconURL() { return m_iconURL; }
+
+    void loadImageFromResource(const char*);
+        
+    ImageDataStatus imageDataStatus();
+    
+    const HashSet<String>& retainingPageURLs() { return m_retainingPageURLs; }
+    
+    IconSnapshot snapshot(bool forDeletion = false) const;
+private:
+    String m_iconURL;
+    time_t m_stamp;
+    OwnPtr<Image> m_image;
+    
+    HashSet<String> m_retainingPageURLs;
+        
+    // This allows us to cache whether or not a SiteIcon has had its data set yet
+    // This helps the IconDatabase know if it has to set the data on a new object or not,
+    // and also to determine if the icon is missing data or if it just hasn't been brought
+    // in from the DB yet
+    bool m_dataSet;
+    
+    // FIXME - Right now WebCore::Image doesn't have a very good API for accessing multiple representations
+    // Even the NSImage way of doing things that we do in WebKit isn't very clean...  once we come up with a 
+    // better way of handling that, we'll likely have a map of size-to-images similar to below
+    // typedef HashMap<IntSize, Image*> SizeImageMap;
+    // SizeImageMap m_images;
+};
+
+
+} //namespace WebCore
+
+#endif
diff --git a/WebCore/loader/icon/PageURLRecord.cpp b/WebCore/loader/icon/PageURLRecord.cpp
new file mode 100644 (file)
index 0000000..e27afcc
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PageURLRecord.h"
+
+#include "IconRecord.h"
+
+namespace WebCore {
+
+PageURLRecord::PageURLRecord(const String& pageURL)
+    : m_pageURL(pageURL)
+    , m_retainCount(0)
+{
+}
+
+void PageURLRecord::setIconRecord(PassRefPtr<IconRecord> icon)
+{
+    ASSERT(icon);
+    if (m_iconRecord)
+        m_iconRecord->m_retainingPageURLs.remove(m_pageURL);
+        
+    m_iconRecord = icon;
+    m_iconRecord->m_retainingPageURLs.add(m_pageURL);
+}
+    
+PageURLSnapshot PageURLRecord::snapshot(bool forDeletion) const 
+{
+    return PageURLSnapshot(m_pageURL, (m_iconRecord && !forDeletion) ? m_iconRecord->iconURL() : String());
+}
+
+} // namespace WebCore
diff --git a/WebCore/loader/icon/PageURLRecord.h b/WebCore/loader/icon/PageURLRecord.h
new file mode 100644 (file)
index 0000000..d4cb4e9
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef PageURLRecord_h
+#define PageURLRecord_h
+
+#include "PlatformString.h"
+
+#include <wtf/Noncopyable.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class IconRecord;
+
+class PageURLSnapshot {
+public:
+    PageURLSnapshot() { }
+    
+    PageURLSnapshot(const String& page, const String& icon)
+        : pageURL(page)
+        , iconURL(icon)
+    { }
+    
+    String pageURL;
+    String iconURL;
+};
+
+class PageURLRecord : Noncopyable {
+public:
+    PageURLRecord(const String& pageURL);
+
+    inline String url() const { return m_pageURL; }
+    
+    void setIconRecord(PassRefPtr<IconRecord>);
+    IconRecord* PageURLRecord::iconRecord() { return m_iconRecord.get(); }
+
+    PageURLSnapshot snapshot(bool forDeletion = false) const;
+
+    inline bool retain() { return ++m_retainCount; }
+
+    inline bool release()
+    {
+        ASSERT(m_retainCount > 0);
+        return --m_retainCount;
+    }
+
+    inline int retainCount() const { return m_retainCount; }
+private:
+    String m_pageURL;
+    RefPtr<IconRecord> m_iconRecord;
+    int m_retainCount;
+};
+
+}
+
+#endif // PageURLRecord_h
index b60c94168f669f5d556abc08bdc28f8ba123a04e..e0edca0962bae3ab0763462d37e0c4f593ac75ea 100644 (file)
@@ -67,6 +67,12 @@ void SharedBuffer::clear()
     m_buffer.resize(0);
 }
 
+PassRefPtr<SharedBuffer> SharedBuffer::copy() const
+{
+    return new SharedBuffer(data(), size());
+}
+
+
 #if !PLATFORM(MAC)
 
 inline void SharedBuffer::clearPlatformData()
index 122ed166050077a4cb5212d77b22e43cf0073af9..a5cbfeb69698c917cdce74684518ebf203d40d17 100644 (file)
@@ -67,6 +67,8 @@ public:
     const char* platformData() const;
     unsigned platformDataSize() const;
 
+    PassRefPtr<SharedBuffer> copy() const;
+    
 private:
     void clearPlatformData();
     void maybeTransferPlatformData();
index cf3e99121c34a30790fd7a54e34c7cab36972945..d42f69f79d818f243eb09f1347ccd5aa25adf5d5 100644 (file)
@@ -274,6 +274,8 @@ public:
     virtual void windowObjectCleared() const {}
     virtual void didPerformFirstNavigation() const {}
 
+    virtual void registerForIconNotification(bool listen) {}
+
 #if PLATFORM(MAC)
     virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long identifier, NSCachedURLResponse* response) const { return response; }
 #endif
index 1581e1da0923e39049a1b7f4a7b9bc077da5202d..0abc6d6d2eb3e011225b372640727842d27faddf 100644 (file)
@@ -71,6 +71,7 @@
 #include "SSLKeyGenerator.h"
 #include "SubresourceLoader.h"
 #include "SystemTime.h"
+#include "Threading.h"
 #include "loader.h"
 #include <stdio.h>
 #include <stdlib.h>
@@ -113,3 +114,5 @@ void CachedPage::close() { notImplemented(); }
 
 Vector<String> WebCore::supportedKeySizes() { notImplemented(); return Vector<String>(); }
 String WebCore::signedPublicKeyAndChallengeString(unsigned, const String&, const KURL&) { notImplemented(); return String(); }
+
+void WebCore::callOnMainThread(void (*)()) { notImplemented(); }
index b797e173d496e01428c03880658df9586491aac3..1c0096c5903890359d98613b9cfa9067136017c0 100644 (file)
@@ -1,3 +1,61 @@
+2007-09-08  Brady Eidson  <beidson@apple.com>
+
+        Reviewed by Darin
+
+        <rdar://problem/5434431> - Asynchronous Icon Database
+
+        WebKit side of things
+        Mainly, there are Notifications WebKit has to listen for now that tell it when to either call back into WebCore
+        for some purpose or to send the webView:didReceiveIcon: delegate call
+
+        Many smaller tweaks as well.
+
+        * Misc/WebIconDatabase.h:
+        * Misc/WebIconDatabase.mm:
+        (defaultClient):
+        (-[WebIconDatabase init]):
+        (+[WebIconDatabase delayDatabaseCleanup]): Accessor so clients can prevent the thread from cleaning up the database
+          before they've done all their necessary retaining of icons.
+        (+[WebIconDatabase allowDatabaseCleanup]):
+        (-[WebIconDatabase removeAllIcons]):
+        (-[WebIconDatabase _isEnabled]):
+        (-[WebIconDatabase _sendNotificationForURL:]):
+        (-[WebIconDatabase _sendDidRemoveAllIconsNotification]):
+        (-[WebIconDatabase _databaseDirectory]):
+
+        (-[ThreadEnabler threadEnablingSelector:]): Quick and dirty class to enabled Cocoa multithreading
+        (+[ThreadEnabler enableThreading]):
+        (importToWebCoreFormat):
+        * Misc/WebIconDatabaseInternal.h: Expose the internal methods of WebIconDatabase that are required by WebIconDatabaseClient
+
+        * Misc/WebNSNotificationCenterExtras.h: Added. - Great utility class whose design was borrowed from Colloquy
+          that allows the posting of a Cocoa notification on the main thread from *any* thread
+        * Misc/WebNSNotificationCenterExtras.m: Added.
+        (-[NSNotificationCenter postNotificationOnMainThreadWithName:object:]):
+        (-[NSNotificationCenter postNotificationOnMainThreadWithName:object:userInfo:]):
+        (-[NSNotificationCenter postNotificationOnMainThreadWithName:object:userInfo:waitUntilDone:]):
+        (+[NSNotificationCenter _postNotificationName:]):
+
+        * WebCoreSupport/WebFrameLoaderClient.h:
+        * WebCoreSupport/WebFrameLoaderClient.mm:
+        (WebFrameLoaderClient::dispatchDidReceiveIcon): Send the webView:didReceiveIcon: delegate call
+        (WebFrameLoaderClient::registerForIconNotification):
+
+        * WebCoreSupport/WebIconDatabaseClient.h: Added.
+        * WebCoreSupport/WebIconDatabaseClient.mm: Added.
+        (WebIconDatabaseClient::performImport):  Perform the Safari 2 icon import
+        (WebIconDatabaseClient::dispatchDidRemoveAllIcons): Send the NSNotification
+        (WebIconDatabaseClient::dispatchDidAddIconForPageURL): Ditto
+
+        * WebView/WebView.mm:
+        (-[WebView _receivedIconChangedNotification:]): Check and see if this notification is for this WebView's current URL by
+          calling back into the IconDatabase
+        (-[WebView _registerForIconNotification:]): Support for WebIconDatabaseClient
+        (-[WebView _dispatchDidReceiveIconFromWebFrame:]): Dispatch this delegate call as well as unregister for the notification
+        * WebView/WebViewInternal.h:
+
+        * WebKit.xcodeproj/project.pbxproj:
+
 2007-09-07  Geoffrey Garen  <ggaren@apple.com>
 
         Suggested by Maciej Stachowiak.
         linearly relative to available space. Caches that have bounded value 
         grow inverse-squaredly relative to available space.
 
+>>>>>>> .r25438
 2007-09-05  Timothy Hatcher  <timothy@apple.com>
 
         Reviewed by Darin.
         * WebView/WebViewInternal.h:
         * WebView/WebViewPrivate.h:
 
+>>>>>>> .r25411
 2007-09-04  Timothy Hatcher  <timothy@apple.com>
 
         Reviewed by Darin.
index 8104f151fda6ba12f7d8d7d9ce4aa9764cc7940b..8dc3d45c642bca80e6fd164caae18c04939f16a2 100644 (file)
@@ -122,6 +122,22 @@ extern NSSize WebIconLargeSize;  // 128 x 128
 */
 - (void)releaseIconForURL:(NSString *)URL;
 
+/*!
+    @method delayDatabaseCleanup:
+    @discussion Only effective if called before the database begins removing icons.
+    delayDatabaseCleanUp increments an internal counter that when 0 begins the database clean-up.
+    The counter equals 0 at initialization.
+*/
++ (void)delayDatabaseCleanup;
+
+/*!
+    @method allowDatabaseCleanup:
+    @discussion Informs the database that it now can begin removing icons.
+    allowDatabaseCleanup decrements an internal counter that when 0 begins the database clean-up.
+    The counter equals 0 at initialization.
+*/
++ (void)allowDatabaseCleanup;
+
 - (void)setDelegate:(id)delegate;
 - (id)delegate;
 
index b1d66eafbc1113bd9a490bcb1fdd567c744b4ed3..2c1b62da5914ba1b11ee8ee1dce5c340cde1dc95 100644 (file)
 
 #import "WebIconDatabaseInternal.h"
 
+#import "WebIconDatabaseClient.h"
 #import "WebIconDatabaseDelegate.h"
 #import "WebKitLogging.h"
 #import "WebKitNSStringExtras.h"
+#import "WebNSNotificationCenterExtras.h"
 #import "WebNSURLExtras.h"
 #import "WebPreferences.h"
 #import "WebTypesInternal.h"
+#import <WebCore/FoundationExtras.h>
 #import <WebCore/IconDatabase.h>
 #import <WebCore/Image.h>
 #import <WebCore/IntSize.h>
@@ -60,18 +63,22 @@ NSSize WebIconLargeSize = {128, 128};
 
 #define UniqueFilePathSize (34)
 
-@interface WebIconDatabase (WebInternal)
+static WebIconDatabaseClient* defaultClient()
+{
+    static WebIconDatabaseClient* defaultClient = new WebIconDatabaseClient();
+    return defaultClient;
+}
+
+@interface WebIconDatabase (WebReallyInternal)
 - (BOOL)_isEnabled;
-- (void)_setIconURL:(NSString *)iconURL forURL:(NSString *)URL;
-- (BOOL)_hasEntryForIconURL:(NSString *)iconURL;
 - (void)_sendNotificationForURL:(NSString *)URL;
+- (void)_sendDidRemoveAllIconsNotification;
 - (NSImage *)_iconForFileURL:(NSString *)fileURL withSize:(NSSize)size;
 - (void)_resetCachedWebPreferences:(NSNotification *)notification;
 - (NSImage *)_largestIconFromDictionary:(NSMutableDictionary *)icons;
 - (NSMutableDictionary *)_iconsBySplittingRepresentationsOfIcon:(NSImage *)icon;
 - (NSImage *)_iconFromDictionary:(NSMutableDictionary *)icons forSize:(NSSize)size cache:(BOOL)cache;
 - (void)_scaleIcon:(NSImage *)icon toSize:(NSSize)size;
-- (void)_importToWebCoreFormat; 
 - (NSString *)_databaseDirectory;
 @end
 
@@ -103,6 +110,7 @@ NSSize WebIconLargeSize = {128, 128};
     iconDatabase()->setEnabled(enabled);
     if (!enabled)
         return self;
+    iconDatabase()->setClient(defaultClient());
     
     // Figure out the directory we should be using for the icon.db
     NSString *databaseDirectory = [self _databaseDirectory];
@@ -117,28 +125,10 @@ NSSize WebIconLargeSize = {128, 128};
             rename([legacyDB fileSystemRepresentation], [newDB fileSystemRepresentation]);
     }
     
-    // Open the WebCore icon database and import the old WebKit icon database
+    // Set the private browsing pref then open the WebCore icon database
+    iconDatabase()->setPrivateBrowsingEnabled([[WebPreferences standardPreferences] privateBrowsingEnabled]);
     if (!iconDatabase()->open(databaseDirectory))
         LOG_ERROR("Unable to open icon database");
-    else if ([self _isEnabled] && !iconDatabase()->imported()) {
-        [self _importToWebCoreFormat];
-        
-#ifndef BUILDING_ON_TIGER 
-        // Tell backup software (i.e., Time Machine) to never back up the icon database, because  
-        // it's a large file that changes frequently, thus using a lot of backup disk space, and 
-        // it's unlikely that many users would be upset about it not being backed up. We do this 
-        // here because this code is only executed once for each icon database instance. We could 
-        // make this configurable on a per-client basis someday if that seemed useful. 
-        // See <rdar://problem/5320208>.
-        CFStringRef databasePath = iconDatabase()->databasePath().createCFString();
-        CFURLRef databasePathURL = CFURLCreateWithFileSystemPath(0, databasePath, kCFURLPOSIXPathStyle, FALSE); 
-        CFRelease(databasePath);
-        CSBackupSetItemExcluded(databasePathURL, true, true); 
-        CFRelease(databasePathURL);
-#endif 
-    }
-
-    iconDatabase()->setPrivateBrowsingEnabled([[WebPreferences standardPreferences] privateBrowsingEnabled]);
     
     // Register for important notifications
     [[NSNotificationCenter defaultCenter] addObserver:self
@@ -222,6 +212,20 @@ NSSize WebIconLargeSize = {128, 128};
     iconDatabase()->releaseIconForPageURL(pageURL);
 }
 
++ (void)delayDatabaseCleanup
+{
+    ASSERT_MAIN_THREAD();
+
+    IconDatabase::delayDatabaseCleanup();
+}
+
++ (void)allowDatabaseCleanup
+{
+    ASSERT_MAIN_THREAD();
+
+    IconDatabase::allowDatabaseCleanup();
+}
+
 - (void)setDelegate:(id)delegate
 {
     _private->delegate = delegate;
@@ -243,11 +247,9 @@ NSSize WebIconLargeSize = {128, 128};
     ASSERT_MAIN_THREAD();
     if (![self _isEnabled])
         return;
+
+    // Via the IconDatabaseClient interface, removeAllIcons() will send the WebIconDatabaseDidRemoveAllIconsNotification
     iconDatabase()->removeAllIcons();
-    // FIXME: This notification won't get sent if WebCore calls removeAllIcons.
-    [[NSNotificationCenter defaultCenter] postNotificationName:WebIconDatabaseDidRemoveAllIconsNotification
-                                                        object:self
-                                                      userInfo:nil];
 }
 
 @end
@@ -265,28 +267,7 @@ NSSize WebIconLargeSize = {128, 128};
 
 - (BOOL)_isEnabled
 {
-    return iconDatabase()->enabled();
-}
-
-- (void)_setIconURL:(NSString *)iconURL forURL:(NSString *)URL
-{
-    ASSERT(iconURL);
-    ASSERT(URL);
-    ASSERT([self _isEnabled]);
-    
-    // If this iconURL already maps to this pageURL, don't bother sending the notification
-    // The WebCore::IconDatabase returns TRUE if we should send the notification, and false if we shouldn't.
-    // This is a measurable win on the iBench - about 1% worth on average
-    if (iconDatabase()->setIconURLForPageURL(iconURL, URL))
-        // FIXME: This notification won't get set when WebCore sets an icon.
-        [self _sendNotificationForURL:URL];
-}
-
-- (BOOL)_hasEntryForIconURL:(NSString *)iconURL;
-{
-    ASSERT([self _isEnabled]);
-
-    return iconDatabase()->hasEntryForIconURL(iconURL);
+    return iconDatabase()->isEnabled();
 }
 
 - (void)_sendNotificationForURL:(NSString *)URL
@@ -295,11 +276,19 @@ NSSize WebIconLargeSize = {128, 128};
     
     NSDictionary *userInfo = [NSDictionary dictionaryWithObject:URL
                                                          forKey:WebIconNotificationUserInfoURLKey];
-    [[NSNotificationCenter defaultCenter] postNotificationName:WebIconDatabaseDidAddIconNotification
+                                                         
+    [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:WebIconDatabaseDidAddIconNotification
                                                         object:self
                                                       userInfo:userInfo];
 }
 
+- (void)_sendDidRemoveAllIconsNotification
+{
+    [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:WebIconDatabaseDidRemoveAllIconsNotification
+                                                        object:self
+                                                      userInfo:nil];
+}
+
 - (void)_applicationWillTerminate:(NSNotification *)notification
 {
     iconDatabase()->close();
@@ -512,12 +501,71 @@ static NSData* iconDataFromPathForIconURL(NSString *databasePath, NSString *icon
     return iconData;
 }
 
-- (void)_importToWebCoreFormat
+- (NSString *)_databaseDirectory
 {
-    ASSERT(_private);
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 
-    // If we've already performed the import once we shouldn't be trying to do it again
-    ASSERT(!iconDatabase()->imported());
+    // Figure out the directory we should be using for the icon.db
+    NSString *databaseDirectory = [defaults objectForKey:WebIconDatabaseDirectoryDefaultsKey];
+    if (!databaseDirectory) {
+        databaseDirectory = WebIconDatabasePath;
+        [defaults setObject:databaseDirectory forKey:WebIconDatabaseDirectoryDefaultsKey];
+    }
+    
+    return [[databaseDirectory stringByExpandingTildeInPath] stringByStandardizingPath];
+}
+
+@end
+
+@implementation WebIconDatabasePrivate
+@end
+
+@interface ThreadEnabler : NSObject {
+}
++ (void)enableThreading;
+
+- (void)threadEnablingSelector:(id)arg;
+@end
+
+@implementation ThreadEnabler
+
+- (void)threadEnablingSelector:(id)arg
+{
+    return;
+}
+
++ (void)enableThreading
+{
+    ThreadEnabler *enabler = [[ThreadEnabler alloc] init];
+    [NSThread detachNewThreadSelector:@selector(threadEnablingSelector:) toTarget:enabler withObject:nil];
+    [enabler release];
+}
+
+@end
+
+bool importToWebCoreFormat()
+{
+    // Since this is running on a secondary POSIX thread and Cocoa cannot be used multithreaded unless an NSThread has been detached,
+    // make sure that happens here for all WebKit clients
+    if (![NSThread isMultiThreaded])
+        [ThreadEnabler enableThreading];
+    ASSERT([NSThread isMultiThreaded]);    
+    
+#ifndef BUILDING_ON_TIGER 
+    // Tell backup software (i.e., Time Machine) to never back up the icon database, because  
+    // it's a large file that changes frequently, thus using a lot of backup disk space, and 
+    // it's unlikely that many users would be upset about it not being backed up. We do this 
+    // here because this code is only executed once for each icon database instance. We could 
+    // make this configurable on a per-client basis someday if that seemed useful. 
+    // See <rdar://problem/5320208>.
+    CFStringRef databasePath = iconDatabase()->databasePath().createCFString();
+    if (databasePath) {
+        CFURLRef databasePathURL = CFURLCreateWithFileSystemPath(0, databasePath, kCFURLPOSIXPathStyle, FALSE); 
+        CFRelease(databasePath);
+        CSBackupSetItemExcluded(databasePathURL, true, true); 
+        CFRelease(databasePathURL);
+    }
+#endif 
 
     // Get the directory the old icon database *should* be in
     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
@@ -548,7 +596,9 @@ static NSData* iconDataFromPathForIconURL(NSString *databasePath, NSString *icon
         iconURL = [pageURLToIconURL objectForKey:url];
         if (!iconURL)
             continue;
-        iconDatabase()->setIconURLForPageURL(iconURL, url);
+        iconDatabase()->importIconURLForPageURL(iconURL, url);
+        if (iconDatabase()->shouldStopThreadActivity())
+            return false;
     }    
 
     // Second, we'll get a list of the unique IconURLs we have
@@ -560,16 +610,16 @@ static NSData* iconDataFromPathForIconURL(NSString *databasePath, NSString *icon
     while ((url = [enumerator nextObject]) != nil) {
         iconData = iconDataFromPathForIconURL(databaseDirectory, url);
         if (iconData)
-            iconDatabase()->setIconDataForIconURL(SharedBuffer::wrapNSData(iconData), url);
+            iconDatabase()->importIconDataForIconURL(SharedBuffer::wrapNSData(iconData), url);
         else {
             // This really *shouldn't* happen, so it'd be good to track down why it might happen in a debug build
             // however, we do know how to handle it gracefully in release
             LOG_ERROR("%@ is marked as having an icon on disk, but we couldn't get the data for it", url);
-            iconDatabase()->setHaveNoIconForIconURL(url);
+            iconDatabase()->importIconDataForIconURL(0, url);
         }
+        if (iconDatabase()->shouldStopThreadActivity())
+            return false;
     }
-
-    iconDatabase()->setImported(true);
     
     // After we're done importing old style icons over to webcore icons, we delete the entire directory hierarchy 
     // for the old icon DB (skipping the new iconDB if it is in the same directory)
@@ -593,27 +643,10 @@ static NSData* iconDataFromPathForIconURL(NSString *databasePath, NSString *icon
     // If the new iconDB wasn't in that directory, we can delete the directory itself
     if (!foundIconDB)
         rmdir([databaseDirectory fileSystemRepresentation]);
-}
-
-- (NSString *)_databaseDirectory
-{
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-
-    // Figure out the directory we should be using for the icon.db
-    NSString *databaseDirectory = [defaults objectForKey:WebIconDatabaseDirectoryDefaultsKey];
-    if (!databaseDirectory) {
-        databaseDirectory = WebIconDatabasePath;
-        [defaults setObject:databaseDirectory forKey:WebIconDatabaseDirectoryDefaultsKey];
-    }
     
-    return [[databaseDirectory stringByExpandingTildeInPath] stringByStandardizingPath];
+    return true;
 }
 
-@end
-
-@implementation WebIconDatabasePrivate
-@end
-
 NSImage *webGetNSImage(Image* image, NSSize size)
 {
     ASSERT_MAIN_THREAD();
index 4b38fe3ba85c391ba73baeed92f51ada22800730..b7b01ecb6985c2278183abbd68907e9aa72c24ed 100644 (file)
@@ -40,4 +40,10 @@ namespace WebCore {
 }
 @end
 
+@interface WebIconDatabase (WebInternal)
+- (void)_sendNotificationForURL:(NSString *)URL;
+- (void)_sendDidRemoveAllIconsNotification;
+@end
+
+extern bool importToWebCoreFormat();
 NSImage *webGetNSImage(WebCore::Image*, NSSize);
diff --git a/WebKit/Misc/WebNSNotificationCenterExtras.h b/WebKit/Misc/WebNSNotificationCenterExtras.h
new file mode 100644 (file)
index 0000000..1a2e16c
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "WebNSNotificationCenterExtras.h"
+
+@interface NSNotificationCenter (WebNSNotificationCenterExtras)
+
+- (void)postNotificationOnMainThreadWithName:(NSString *)name object:(id)object;
+- (void)postNotificationOnMainThreadWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo;
+- (void)postNotificationOnMainThreadWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo waitUntilDone:(BOOL)wait;
+
++ (void)_postNotificationName:(NSDictionary *)info;
+
+@end
diff --git a/WebKit/Misc/WebNSNotificationCenterExtras.m b/WebKit/Misc/WebNSNotificationCenterExtras.m
new file mode 100644 (file)
index 0000000..bc5b4ed
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "WebNSNotificationCenterExtras.h"
+
+@implementation NSNotificationCenter (WebNSNotificationCenterExtras)
+
+- (void)postNotificationOnMainThreadWithName:(NSString *)name object:(id)object 
+{
+    [self postNotificationOnMainThreadWithName:name object:object userInfo:nil waitUntilDone:NO];
+}
+
+- (void)postNotificationOnMainThreadWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo
+{
+    [self postNotificationOnMainThreadWithName:name object:object userInfo:userInfo waitUntilDone:NO];
+}
+
+- (void)postNotificationOnMainThreadWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo waitUntilDone:(BOOL)wait
+{
+    NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithCapacity:3];
+    if (name) 
+        [info setObject:name forKey:@"name"];
+
+    if (object) 
+        [info setObject:object forKey:@"object"];
+
+    if (userInfo) 
+        [info setObject:userInfo forKey:@"userInfo"];
+
+    [[self class] performSelectorOnMainThread:@selector(_postNotificationName:) withObject:info waitUntilDone:wait];
+}
+
++ (void)_postNotificationName:(NSDictionary *)info 
+{
+    NSString *name = [info objectForKey:@"name"];
+    id object = [info objectForKey:@"object"];
+    NSDictionary *userInfo = [info objectForKey:@"userInfo"];
+
+    [[self defaultCenter] postNotificationName:name object:object userInfo:userInfo];
+
+    [info release];
+}
+
+@end
index a883d745b3c6320f3da7509dc84c9e30c1d88e44..c5e4a340ee25460048e0267c7343e13944cee510 100644 (file)
@@ -197,6 +197,8 @@ private:
     virtual void windowObjectCleared() const;
     virtual void didPerformFirstNavigation() const;
 
+    virtual void registerForIconNotification(bool listen);
+
     void deliverArchivedResourcesAfterDelay() const;
     bool canUseArchivedResource(NSURLRequest *) const;
     bool canUseArchivedResource(NSURLResponse *) const;
index 1e0c1498985853db7be810f9ab5444b4db3080d1..ea5958d25a1a1f1008bd76cb2abbcf4dfea5febf 100644 (file)
@@ -500,17 +500,7 @@ void WebFrameLoaderClient::dispatchDidReceiveIcon()
     ASSERT([m_webFrame.get() _isMainFrame]);
     WebView *webView = getWebView(m_webFrame.get());   
 
-    // FIXME: This willChangeValueForKey call is too late, because the icon has already changed by now.
-    [webView _willChangeValueForKey:_WebMainFrameIconKey];
-
-    WebFrameLoadDelegateImplementationCache implementations = WebViewGetFrameLoadDelegateImplementations(webView);
-    if (implementations.didReceiveIconForFrameFunc) {
-        Image* image = iconDatabase()->iconForPageURL(core(m_webFrame.get())->loader()->url().url(), IntSize(16, 16));
-        if (NSImage *icon = webGetNSImage(image, NSMakeSize(16, 16)))
-            CallFrameLoadDelegate(implementations.didReceiveIconForFrameFunc, webView, @selector(webView:didReceiveIcon:forFrame:), icon, m_webFrame.get());
-    }
-
-    [webView _didChangeValueForKey:_WebMainFrameIconKey];
+    [webView _dispatchDidReceiveIconFromWebFrame:m_webFrame.get()];
 }
 
 void WebFrameLoaderClient::dispatchDidStartProvisionalLoad()
@@ -1241,6 +1231,11 @@ void WebFrameLoaderClient::windowObjectCleared() const
     [m_webFrame->_private->bridge windowObjectCleared];
 }
 
+void WebFrameLoaderClient::registerForIconNotification(bool listen)
+{
+    [[m_webFrame.get() webView] _registerForIconNotification:listen];
+}
+
 void WebFrameLoaderClient::didPerformFirstNavigation() const
 {
     WebPreferences *preferences = [[m_webFrame.get() webView] preferences];
diff --git a/WebKit/WebCoreSupport/WebIconDatabaseClient.h b/WebKit/WebCoreSupport/WebIconDatabaseClient.h
new file mode 100644 (file)
index 0000000..e908242
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <WebCore/IconDatabaseClient.h>
+
+namespace WebCore {
+    class String;
+}
+
+class WebIconDatabaseClient : public WebCore::IconDatabaseClient {
+public:
+    virtual bool performImport();
+    virtual void dispatchDidRemoveAllIcons();
+    virtual void dispatchDidAddIconForPageURL(const WebCore::String& pageURL);
+};
diff --git a/WebKit/WebCoreSupport/WebIconDatabaseClient.mm b/WebKit/WebCoreSupport/WebIconDatabaseClient.mm
new file mode 100644 (file)
index 0000000..43202b0
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "WebIconDatabaseClient.h"
+
+#import "WebIconDatabaseInternal.h"
+
+#import <WebCore/FoundationExtras.h>
+#import <WebCore/PlatformString.h>
+
+
+bool WebIconDatabaseClient::performImport()
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    bool result = importToWebCoreFormat();
+    [pool drain];
+    return result;
+}
+
+void WebIconDatabaseClient::dispatchDidRemoveAllIcons()
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    [[WebIconDatabase sharedIconDatabase] _sendDidRemoveAllIconsNotification];
+    [pool drain];
+}
+
+void WebIconDatabaseClient::dispatchDidAddIconForPageURL(const WebCore::String& pageURL)
+{
+    // This is a quick notification that is likely to fire in a rapidly iterating loop
+    // Therefore we let WebCore handle autorelease by draining its pool "from time to time"
+    // instead of us doing it every iteration
+    [[WebIconDatabase sharedIconDatabase] _sendNotificationForURL:pageURL];
+}
index adbe7d95384dc131b084940cd2d13f9848d937ec..f41b2ecc04ddee0c0f371952d8df78748fe036de 100644 (file)
                22F219CC08D236730030E078 /* WebBackForwardListPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 22F219CB08D236730030E078 /* WebBackForwardListPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4BF99F900AE050BC00815C2B /* WebEditorClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BF99F8E0AE050BC00815C2B /* WebEditorClient.h */; settings = {ATTRIBUTES = (); }; };
                4BF99F910AE050BC00815C2B /* WebEditorClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BF99F8F0AE050BC00815C2B /* WebEditorClient.mm */; };
+               51494CD60C7EBDE0004178C5 /* WebIconDatabaseClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 51494CD40C7EBDE0004178C5 /* WebIconDatabaseClient.h */; };
+               51494CD70C7EBDE0004178C5 /* WebIconDatabaseClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51494CD50C7EBDE0004178C5 /* WebIconDatabaseClient.mm */; };
+               51494D240C7EC1B7004178C5 /* WebNSNotificationCenterExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 51494D220C7EC1B6004178C5 /* WebNSNotificationCenterExtras.h */; };
+               51494D250C7EC1B7004178C5 /* WebNSNotificationCenterExtras.m in Sources */ = {isa = PBXBuildFile; fileRef = 51494D230C7EC1B7004178C5 /* WebNSNotificationCenterExtras.m */; };
                51B2A1000ADB15D0002A9BEE /* WebIconDatabaseDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 51B2A0FF0ADB15D0002A9BEE /* WebIconDatabaseDelegate.h */; };
                51C714FB0B20F79F00E5E33C /* WebBackForwardListInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 51C714FA0B20F79F00E5E33C /* WebBackForwardListInternal.h */; };
                51FDC4D30B0AF5C100F84EB3 /* WebHistoryItemPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 51FDC4D20B0AF5C100F84EB3 /* WebHistoryItemPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
                51443F9A0429392B00CA2D3A /* WebPolicyDelegate.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = WebPolicyDelegate.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                51443F9B0429392B00CA2D3A /* WebPolicyDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebPolicyDelegate.mm; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                51443F9C0429392B00CA2D3A /* WebPolicyDelegatePrivate.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = WebPolicyDelegatePrivate.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
+               51494CD40C7EBDE0004178C5 /* WebIconDatabaseClient.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = WebIconDatabaseClient.h; sourceTree = "<group>"; };
+               51494CD50C7EBDE0004178C5 /* WebIconDatabaseClient.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = WebIconDatabaseClient.mm; sourceTree = "<group>"; };
+               51494D220C7EC1B6004178C5 /* WebNSNotificationCenterExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebNSNotificationCenterExtras.h; sourceTree = "<group>"; };
+               51494D230C7EC1B7004178C5 /* WebNSNotificationCenterExtras.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebNSNotificationCenterExtras.m; sourceTree = "<group>"; };
                5152FADD033FC50400CA2ACD /* WebDefaultContextMenuDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = WebDefaultContextMenuDelegate.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                5152FADE033FC50400CA2ACD /* WebDefaultContextMenuDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebDefaultContextMenuDelegate.mm; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                5152FADF033FC50400CA2ACD /* WebDefaultPolicyDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = WebDefaultPolicyDelegate.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                                65EEDE56084FFC9E0002DB25 /* WebNSFileManagerExtras.m */,
                                8398847A03426FB000BC5F5E /* WebNSImageExtras.h */,
                                8398847B03426FB000BC5F5E /* WebNSImageExtras.m */,
+                               51494D220C7EC1B6004178C5 /* WebNSNotificationCenterExtras.h */,
+                               51494D230C7EC1B7004178C5 /* WebNSNotificationCenterExtras.m */,
                                93D1FE13067EB10B009CE68A /* WebNSObjectExtras.h */,
                                ED2B2474033A2DA800C1A526 /* WebNSPasteboardExtras.h */,
                                ED2B2475033A2DA800C1A526 /* WebNSPasteboardExtras.mm */,
                                F5AFB45F02B94DC8018635CA /* WebFrameBridge.mm */,
                                931633EA0AEDFF930062B92D /* WebFrameLoaderClient.h */,
                                931633EE0AEDFFAE0062B92D /* WebFrameLoaderClient.mm */,
+                               51494CD40C7EBDE0004178C5 /* WebIconDatabaseClient.h */,
+                               51494CD50C7EBDE0004178C5 /* WebIconDatabaseClient.mm */,
                                9CE1F8A302A5C6F30ECA2ACD /* WebImageRendererFactory.m */,
                                06693DDA0BFBA85200216072 /* WebInspectorClient.h */,
                                06693DDB0BFBA85200216072 /* WebInspectorClient.mm */,
                                06693DDC0BFBA85200216072 /* WebInspectorClient.h in Headers */,
                                BCDFA8F90C10B6F500D3A10C /* WebKitPluginContainerView.h in Headers */,
                                5D7BF8140C2A1D90008CE06D /* WebInspector.h in Headers */,
+                               51494CD60C7EBDE0004178C5 /* WebIconDatabaseClient.h in Headers */,
+                               51494D240C7EC1B7004178C5 /* WebNSNotificationCenterExtras.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                06693DDD0BFBA85200216072 /* WebInspectorClient.mm in Sources */,
                                BCDFA9130C10B93E00D3A10C /* WebKitPluginContainerView.mm in Sources */,
                                5D7BF8150C2A1D90008CE06D /* WebInspector.mm in Sources */,
+                               51494CD70C7EBDE0004178C5 /* WebIconDatabaseClient.mm in Sources */,
+                               51494D250C7EC1B7004178C5 /* WebNSNotificationCenterExtras.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index 279355d314d1d7a78c8cf9e41315b7d1c4ba6114..c98669ed360d75937fd6d7d06fef3232e1ca09d3 100644 (file)
@@ -58,6 +58,7 @@
 #import "WebHTMLViewInternal.h"
 #import "WebHistoryItemInternal.h"
 #import "WebIconDatabase.h"
+#import "WebIconDatabaseInternal.h"
 #import "WebInspectorClient.h"
 #import "WebKitErrors.h"
 #import "WebKitLogging.h"
@@ -4036,6 +4037,47 @@ static WebFrameView *containingFrameView(NSView *view)
     return _private->becomingFirstResponderFromOutside;
 }
 
+- (void)_receivedIconChangedNotification:(NSNotification *)notification
+{
+    // Get the URL for this notification
+    NSDictionary *userInfo = [notification userInfo];
+    ASSERT([userInfo isKindOfClass:[NSDictionary class]]);
+    NSString *urlString = [userInfo objectForKey:WebIconNotificationUserInfoURLKey];
+    ASSERT([urlString isKindOfClass:[NSString class]]);
+    
+    // If that URL matches the current main frame, dispatch the delegate call, which will also unregister
+    // us for this notification
+    if ([[self mainFrameURL] isEqualTo:urlString])
+        [self _dispatchDidReceiveIconFromWebFrame:[self mainFrame]];
+}
+
+- (void)_registerForIconNotification:(BOOL)listen
+{
+    if (listen)
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_receivedIconChangedNotification:) name:WebIconDatabaseDidAddIconNotification object:nil];        
+    else
+        [[NSNotificationCenter defaultCenter] removeObserver:self name:WebIconDatabaseDidAddIconNotification object:nil];
+}
+
+- (void)_dispatchDidReceiveIconFromWebFrame:(WebFrame *)webFrame
+{
+    // FIXME: This willChangeValueForKey call is too late, because the icon has already changed by now.
+    [self _willChangeValueForKey:_WebMainFrameIconKey];
+    
+    // Since we definitely have an icon and are about to send out the delegate call for that, this WebView doesn't need to listen for the general
+    // notification any longer
+    [self _registerForIconNotification:NO];
+
+    WebFrameLoadDelegateImplementationCache implementations = WebViewGetFrameLoadDelegateImplementations(self);
+    if (implementations.didReceiveIconForFrameFunc) {
+        Image* image = iconDatabase()->iconForPageURL(core(webFrame)->loader()->url().url(), IntSize(16, 16));
+        if (NSImage *icon = webGetNSImage(image, NSMakeSize(16, 16)))
+            CallFrameLoadDelegate(implementations.didReceiveIconForFrameFunc, self, @selector(webView:didReceiveIcon:forFrame:), icon, webFrame);
+    }
+
+    [self _didChangeValueForKey:_WebMainFrameIconKey];
+}
+
 - (NSString *)_userVisibleBundleVersionFromFullVersion:(NSString *)fullVersion
 {
     // If the version is 4 digits long or longer, then the first digit represents
index cb3cc31519dc8a14ee867ed186af9a29457a5c59..e67ee13ee4e15b1dda541fb2cf67220a7e4a7097 100644 (file)
@@ -111,6 +111,9 @@ typedef WebCore::Page WebCorePage;
 - (void)_removeObjectForIdentifier:(unsigned long)identifier;
 - (BOOL)_becomingFirstResponderFromOutside;
 
+- (void)_registerForIconNotification:(BOOL)listen;
+- (void)_dispatchDidReceiveIconFromWebFrame:(WebFrame *)webFrame;
+
 @end
 
 typedef struct _WebResourceDelegateImplementationCache {
index 47449c61b96fad646e6f0a0e7cd36f4324b6e385..33373f3af8d0ca9e5a96f9cf895db02231bdd0c6 100644 (file)
@@ -1,3 +1,12 @@
+2007-09-08  Brady Eidson  <beidson@apple.com>
+
+        <rdar://problem/5434431> - Asynchronous Icon Database
+
+        * WebFrame.cpp:
+        (WebFrame::didPerformFirstNavigation): Empty impl for now
+        (WebFrame::registerForIconNotification): Ditto
+        * WebFrame.h:
+
 2007-09-05  Geoffrey Garen  <ggaren@apple.com>
 
         Reviewed by Darin Adler, Maciej Stachowiak, Mark Rowe, Tim Hatcher.
index 505de5af1d5b7d7abd0618a20ef602883c302853..c87b0372b232d11b01913d255306936c32ec4ade 100644 (file)
@@ -2341,7 +2341,11 @@ void WebFrame::windowObjectCleared() const
     }
 }
 
-void FrameLoaderClient::didPerformFirstNavigation() const
+void WebFrame::didPerformFirstNavigation() const
+{
+}
+
+void WebFrame::registerForIconNotification(bool /*listen*/)
 {
 }
 
index 6b38d89e03dd162909a6018046fe4fb2593e17fb..9619c7524623e36aceaafd84fb5151c45d610c1c 100644 (file)
@@ -314,6 +314,8 @@ public:
 
     virtual void windowObjectCleared() const;
     virtual void didPerformFirstNavigation() const;
+    
+    virtual void registerForIconNotification(bool listen);
 
     // WebFrame
     void initWithWebFrameView(IWebFrameView*, IWebView*, WebCore::Page*, WebCore::HTMLFrameOwnerElement*);