+2006-06-27 Brady Eidson <beidson@apple.com>
+
+ Reviewed by Maciej
+
+ Hookup the new semi-functional SQLite icon database.
+ For now, it is living side-by-side with the old DB so one can compare the
+ two for debugging purposes. Also, it is disabled (in WebKit) by default unless you
+ compile with ICONDEBUG #defined.
+
+ * WebCore.xcodeproj/project.pbxproj: Added file SiteIcon.cpp
+
+ * bridge/mac/WebCoreIconDatabaseBridge.h:
+ * bridge/mac/WebCoreIconDatabaseBridge.mm:
+ (-[WebCoreIconDatabaseBridge privateBrowsingEnabled]):
+ (-[WebCoreIconDatabaseBridge iconForPageURL:withSize:]):
+ (-[WebCoreIconDatabaseBridge iconURLForPageURL:]):
+ (-[WebCoreIconDatabaseBridge _setIconData:forIconURL:]):
+
+ * icon/IconDatabase.cpp:
+ (WebCore::IconDatabase::isValidDatabase):
+ (WebCore::IconDatabase::recreateDatabase):
+ (WebCore::IconDatabase::createPrivateTables):
+ (WebCore::IconDatabase::deletePrivateTables):
+ (WebCore::IconDatabase::imageDataForIconID):
+ (WebCore::IconDatabase::imageDataForIconURL):
+ (WebCore::IconDatabase::imageDataForPageURL):
+ (WebCore::IconDatabase::setPrivateBrowsingEnabled):
+ (WebCore::IconDatabase::iconForPageURL):
+ (WebCore::IconDatabase::iconURLForURL):
+ (WebCore::IconDatabase::setIconDataForIconURL):
+ (WebCore::IconDatabase::establishIconIDForEscapedIconURL):
+ (WebCore::IconDatabase::setHaveNoIconForIconURL):
+ (WebCore::IconDatabase::setIconURLForPageURL):
+ (WebCore::IconDatabase::hasIconForIconURL):
+ * icon/IconDatabase.h:
+ (WebCore::SiteIcon::getIconURL):
+ (WebCore::IconDatabase::getPrivateBrowsingEnabled):
+
+ * icon/SiteIcon.cpp: Added.
+ (SiteIcon::SiteIcon):
+ (SiteIcon::~SiteIcon):
+ (SiteIcon::getImage):
+ (SiteIcon::resetExpiration):
+ (SiteIcon::getExpiration):
+
2006-06-27 Rob Buis <buis@kde.org>
Reviewed by Darin.
5126E6BC0A2E3B12005C29FA /* IconDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = 5126E6BA0A2E3B12005C29FA /* IconDatabase.h */; settings = {ATTRIBUTES = (Private, ); }; };
5126E6BF0A2E3B29005C29FA /* WebCoreIconDatabaseBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = 5126E6BD0A2E3B29005C29FA /* WebCoreIconDatabaseBridge.h */; settings = {ATTRIBUTES = (Private, ); }; };
5126E6C00A2E3B29005C29FA /* WebCoreIconDatabaseBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5126E6BE0A2E3B29005C29FA /* WebCoreIconDatabaseBridge.mm */; };
+ 516149ED0A525E3A003DFC7A /* SiteIcon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 516149EC0A525E3A003DFC7A /* SiteIcon.cpp */; };
51D3EA160A3D24D300BADA35 /* SQLDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51D3EA130A3D24D300BADA35 /* SQLDatabase.cpp */; };
51D3EA170A3D24D300BADA35 /* SQLDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = 51D3EA140A3D24D300BADA35 /* SQLDatabase.h */; };
51D3EA180A3D24D300BADA35 /* SQLStatement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51D3EA150A3D24D300BADA35 /* SQLStatement.cpp */; };
FAE04190097596C9000540BE /* SVGImageLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE0418E097596C9000540BE /* SVGImageLoader.h */; };
/* End PBXBuildFile section */
-/* Begin PBXBuildStyle section */
- BCEECFD10A50A9CE00443D9A /* Development */ = {
- isa = PBXBuildStyle;
- buildSettings = {
- COPY_PHASE_STRIP = NO;
- };
- name = Development;
- };
- BCEECFD20A50A9CE00443D9A /* Deployment */ = {
- isa = PBXBuildStyle;
- buildSettings = {
- COPY_PHASE_STRIP = YES;
- };
- name = Deployment;
- };
-/* End PBXBuildStyle section */
-
/* Begin PBXContainerItemProxy section */
DD041FF009D9E3250010AF2A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
5126E6BE0A2E3B29005C29FA /* WebCoreIconDatabaseBridge.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = WebCoreIconDatabaseBridge.mm; sourceTree = "<group>"; };
5150C2A10702629000AF642C /* WebDashboardRegion.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = WebDashboardRegion.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
5150C2A50702629800AF642C /* WebDashboardRegion.m */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = WebDashboardRegion.m; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
+ 516149EC0A525E3A003DFC7A /* SiteIcon.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SiteIcon.cpp; sourceTree = "<group>"; };
51D3EA130A3D24D300BADA35 /* SQLDatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SQLDatabase.cpp; sourceTree = "<group>"; };
51D3EA140A3D24D300BADA35 /* SQLDatabase.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SQLDatabase.h; sourceTree = "<group>"; };
51D3EA150A3D24D300BADA35 /* SQLStatement.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SQLStatement.cpp; sourceTree = "<group>"; };
5126E6B60A2E3AEF005C29FA /* icon */ = {
isa = PBXGroup;
children = (
+ 516149EC0A525E3A003DFC7A /* SiteIcon.cpp */,
5126E6B90A2E3B12005C29FA /* IconDatabase.cpp */,
5126E6BA0A2E3B12005C29FA /* IconDatabase.h */,
51D3EA130A3D24D300BADA35 /* SQLDatabase.cpp */,
0867D690FE84028FC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 149C284308902B11008A9EFC /* Build configuration list for PBXProject "WebCore" */;
- buildSettings = {
- };
- buildStyles = (
- BCEECFD10A50A9CE00443D9A /* Development */,
- BCEECFD20A50A9CE00443D9A /* Deployment */,
- );
hasScannedForEncodings = 1;
knownRegions = (
English,
51F11E150A48C2920034A24E /* SQLTransaction.cpp in Sources */,
BCCD74E50A4C8DDF005FDA6D /* HTMLViewSourceDocument.cpp in Sources */,
E1052C320A4D70010072D99B /* DOMEventsNonstandard.mm in Sources */,
+ 516149ED0A525E3A003DFC7A /* SiteIcon.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
- (void)closeSharedDatabase;
- (BOOL)isOpen;
-- (NSImage *)iconForURL:(NSString *)url withSize:(NSSize)size;
-- (NSString *)iconURLForURL:(NSString *)url;
+- (NSImage *)iconForPageURL:(NSString *)url withSize:(NSSize)size;
+- (NSString *)iconURLForPageURL:(NSString *)url;
- (NSImage *)defaultIconWithSize:(NSSize)size;
- (void)retainIconForURL:(NSString *)url;
- (void)releaseIconForURL:(NSString *)url;
-- (void)setPrivateBrowsingEnabled:(BOOL)flag;
+- (void)setPrivateBrowsingEnabled:(BOOL)flag;
+- (BOOL)privateBrowsingEnabled;
- (void)_setIconData:(NSData *)data forIconURL:(NSString *)iconURL;
- (void)_setHaveNoIconForIconURL:(NSString *)iconURL;
_iconDB->setPrivateBrowsingEnabled(flag);
}
-- (NSImage *)iconForURL:(NSString *)url withSize:(NSSize)size;
+- (BOOL)privateBrowsingEnabled;
+{
+ if (_iconDB)
+ return _iconDB->getPrivateBrowsingEnabled();
+ return false;
+}
+
+- (NSImage *)iconForPageURL:(NSString *)url withSize:(NSSize)size;
{
ASSERT(_iconDB);
ASSERT(url);
ASSERT(size.width);
ASSERT(size.height);
-
- Image* image = _iconDB->iconForURL(String(url), IntSize(size));
- if (image)
- return image->getNSImage();
+
+ // FIXME - We're doing the resize here for now because WebCore::Image doesn't yet support resizing/multiple representations
+ // This makes it so there's effectively only one size of a particular icon in the system at a time. We really need to move this
+ // to WebCore::Image and WebCore::IconDatabase asap
+ Image* image = _iconDB->iconForPageURL(String(url), IntSize(size));
+ if (image) {
+ NSImage* nsImage = image->getNSImage();
+ ASSERT(nsImage);
+
+ LOG(IconDatabase, "Found %i representations in the NSImage", [[nsImage representations] count]);
+
+ if (!NSEqualSizes([nsImage size], size)) {
+ [nsImage setScalesWhenResized:YES];
+ [nsImage setSize:size];
+ }
+ return nsImage;
+ }
return nil;
}
-- (NSString *)iconURLForURL:(NSString *)url;
+- (NSString *)iconURLForPageURL:(NSString *)url;
{
ASSERT(_iconDB);
ASSERT(url);
ASSERT(data);
ASSERT(iconURL);
- Image* image = new Image();
- image->setNativeData((CFDataRef)data, true);
- _iconDB->setIconForIconURL(image, String(iconURL));
+ _iconDB->setIconDataForIconURL([data bytes], [data length], String(iconURL));
}
- (void)_setHaveNoIconForIconURL:(NSString *)iconURL;
*/
#include "IconDatabase.h"
+#include "Image.h"
#include "Logging.h"
#include "PlatformString.h"
#include <sys/stat.h>
#include <errno.h>
+// FIXME - Make sure we put a private browsing consideration in that uses the temporary tables anytime private browsing would be an issue.
+
const char* DefaultIconDatabaseFilename = "/icon.db";
namespace WebCore {
return false;
}
- String query = "SELECT value FROM IconDatabaseInfo WHERE key = 'Version';";
-
- SQLStatement sql(m_db, query);
- sql.prepare();
- sql.step();
- if (sql.getColumnInt(0) < currentDatabaseVersion) {
+ if (SQLStatement(m_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;
}
-
+
return true;
}
void IconDatabase::recreateDatabase()
{
if (!m_db.executeCommand("CREATE TABLE IconDatabaseInfo (key varchar NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value NOT NULL ON CONFLICT FAIL);")) {
- LOG_ERROR("Could not create IconDatabaseInfo table in icon.db (%i)\n%s", m_db.lastError(), m_db.lastErrorMsg());
+ LOG_ERROR("Could not create IconDatabaseInfo table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
m_db.close();
return;
}
if (!m_db.executeCommand(String("INSERT INTO IconDatabaseInfo VALUES ('Version', ") + String::number(currentDatabaseVersion) + ");")) {
- LOG_ERROR("Could not insert icon database version into IconDatabaseInfo table (%i)\n%s", m_db.lastError(), m_db.lastErrorMsg());
+ LOG_ERROR("Could not insert icon database version into IconDatabaseInfo table (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
m_db.close();
return;
}
if (!m_db.executeCommand("CREATE TABLE PageURL (url varchar NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,iconID integer NOT NULL ON CONFLICT FAIL);")) {
- LOG_ERROR("Could not create PageURL table in icon.db (%i)\n%s", m_db.lastError(), m_db.lastErrorMsg());
+ LOG_ERROR("Could not create PageURL table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
m_db.close();
return;
}
- if (!m_db.executeCommand("CREATE TABLE Icon (iconid INTEGER PRIMARY KEY AUTOINCREMENT, url varchar NOT NULL UNIQUE ON CONFLICT FAIL);")) {
- LOG_ERROR("Could not create Icon table in icon.db (%i)\n%s", m_db.lastError(), m_db.lastErrorMsg());
+ if (!m_db.executeCommand("CREATE TABLE Icon (iconID INTEGER PRIMARY KEY AUTOINCREMENT, url NOT NULL UNIQUE ON CONFLICT FAIL, expires INTEGER);")) {
+ LOG_ERROR("Could not create Icon table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
m_db.close();
return;
}
- if (!m_db.executeCommand("CREATE TABLE IconResource (iconID integer NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,data blob NOT NULL ON CONFLICT FAIL);")) {
- LOG_ERROR("Could not create IconResource table in icon.db (%i)\n%s", m_db.lastError(), m_db.lastErrorMsg());
+ if (!m_db.executeCommand("CREATE TABLE IconResource (iconID integer NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,data blob, touch);")) {
+ LOG_ERROR("Could not create IconResource table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
+ m_db.close();
+ return;
+ }
+ if (!m_db.executeCommand("CREATE TRIGGER create_icon_resource AFTER INSERT ON Icon BEGIN INSERT INTO IconResource (iconID, data) VALUES (new.iconID, NULL); END;")) {
+ LOG_ERROR("Unable to create create_icon_resource trigger in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
m_db.close();
return;
}
}
-void IconDatabase::setPrivateBrowsingEnabled(bool flag)
+void IconDatabase::createPrivateTables()
+{
+ if (!m_db.executeCommand("CREATE TEMP TABLE TempPageURL (url varchar NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,iconID integer NOT NULL ON CONFLICT FAIL);"))
+ LOG_ERROR("Could not create TempPageURL table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
+
+ if (!m_db.executeCommand("CREATE TEMP TABLE TempIcon (iconID INTEGER PRIMARY KEY AUTOINCREMENT, url NOT NULL UNIQUE ON CONFLICT FAIL, expires INTEGER);"))
+ LOG_ERROR("Could not create TempIcon table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
+
+ if (!m_db.executeCommand("CREATE TEMP TABLE TempIconResource (iconID integer NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,data blob, touch);"))
+ LOG_ERROR("Could not create TempIconResource table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
+
+ if (!m_db.executeCommand("CREATE TEMP TRIGGER temp_create_icon_resource AFTER INSERT ON TempIcon BEGIN INSERT INTO TempIconResource (iconID, data) VALUES (new.iconID, NULL); END;"))
+ LOG_ERROR("Unable to create temp_create_icon_resource trigger in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
+}
+
+void IconDatabase::deletePrivateTables()
{
- //FIXME - set/clear private browsing caches
- m_privateBrowsingEnabled = flag;
+ if (!m_db.executeCommand("DROP TEMP TABLE TempPageURL;"))
+ LOG_ERROR("Could not drop TempPageURL table");
+ if (!m_db.executeCommand("DROP TEMP TABLE TempIcon;"))
+ LOG_ERROR("Could not drop TempIcon table");
+ if (!m_db.executeCommand("DROP TEMP TABLE TempIconResource;"))
+ LOG_ERROR("Could not drop TempIconResource table");
}
-Image* IconDatabase::iconForURL(const String& url, const IntSize& size, bool cache)
+const void* IconDatabase::imageDataForIconID(int id, int& size)
{
+ char* query = sqlite3_mprintf("SELECT data FROM IconResource WHERE iconid = %i", id);
+ SQLStatement sql(m_db, String(query));
+ sql.prepare();
+ sqlite3_free(query);
+ if (sql.step() != SQLITE_ROW) {
+ size = 0;
+ return 0;
+ }
+ const void* blob = sql.getColumnBlob(0, size);
+ if (!blob) {
+ size = 0;
+ return 0;
+ }
+ return blob;
+}
+
+const void* IconDatabase::imageDataForIconURL(const String& _iconURL, int& size)
+{
+ //Escape single quotes for SQL
+ String iconURL = _iconURL;
+ iconURL.replace('\'', "''");
+
+ const void* blob = 0;
+
+ // 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) {
+ blob = SQLStatement(m_db, "SELECT TempIconResource.data FROM TempIconResource, TempIcon WHERE TempIcon.url = '" + iconURL + "' AND TempIconResource.iconID = TempIcon.iconID;").getColumnBlob(0,size);
+ if (blob)
+ return blob;
+ }
+
+ // It wasn't found there, so lets check the main tables
+ blob = SQLStatement(m_db, "SELECT IconResource.data FROM IconResource, Icon WHERE Icon.url = '" + iconURL + "' AND IconResource.iconID = Icon.iconID;").getColumnBlob(0, size);
+ if (blob)
+ return blob;
+ size = 0;
return 0;
}
-String IconDatabase::iconURLForURL(const String& url)
+const void* IconDatabase::imageDataForPageURL(const String& _pageURL, int& size)
{
- return "(null)";
+ //Escape single quotes for SQL
+ String pageURL = _pageURL;
+ pageURL.replace('\'', "''");
+
+ const void* blob = 0;
+
+ // 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) {
+ blob = SQLStatement(m_db, "SELECT TempIconResource.data FROM TempIconResource, TempPageURL WHERE TempPageURL.url = '" + pageURL + "' AND TempIconResource.iconID = TempPageURL.iconID;").getColumnBlob(0,size);
+ if (blob)
+ return blob;
+ }
+
+ // It wasn't found there, so lets check the main tables
+ blob = SQLStatement(m_db, "SELECT IconResource.data FROM IconResource, PageURL WHERE PageURL.url = '" + pageURL + "' AND IconResource.iconID = PageURL.iconID;").getColumnBlob(0, size);
+ if (blob)
+ return blob;
+ size = 0;
+ return 0;
+}
+
+void IconDatabase::setPrivateBrowsingEnabled(bool flag)
+{
+ if (m_privateBrowsingEnabled == flag)
+ return;
+
+ m_privateBrowsingEnabled = flag;
+
+ if (!m_privateBrowsingEnabled)
+ deletePrivateTables();
+ else
+ createPrivateTables();
+}
+
+Image* IconDatabase::iconForPageURL(const String& url, const IntSize& size, bool cache)
+{
+ // FIXME - Private browsing
+
+ // We may have a SiteIcon for this specific URL...
+ if (m_pageURLToSiteIcons.contains(url))
+ return m_pageURLToSiteIcons.get(url)->getImage(size);
+
+ // ...and we may have one for the IconURL this URL maps to
+ String iconURL = iconURLForURL(url);
+ if (m_iconURLToSiteIcons.contains(iconURL))
+ return m_iconURLToSiteIcons.get(iconURL)->getImage(size);
+
+ // If we don't have either, we have to create the SiteIcon
+ if (!iconURL.isEmpty()) {
+ SiteIcon* icon = new SiteIcon(iconURL);
+ m_pageURLToSiteIcons.set(url, icon);
+ m_iconURLToSiteIcons.set(iconURL, icon);
+ return icon->getImage(size);
+ }
+
+ return 0;
+}
+
+String IconDatabase::iconURLForURL(const String& _url)
+{
+ String pageURL = _url;
+ pageURL.replace('\'', "''");
+
+ return SQLStatement(m_db, "SELECT Icon.url FROM Icon, PageURL WHERE PageURL.url = '" + pageURL + "' AND Icon.iconID = PageURL.iconID").getColumnText16(0);
}
Image* IconDatabase::defaultIcon(const IntSize& size)
}
-void IconDatabase::setIconForIconURL(Image* icon, const String& url)
+void IconDatabase::setIconDataForIconURL(const void* data, int size, const String& _iconURL)
{
+ ASSERT(size > -1);
+ if (size)
+ ASSERT(data);
+ else
+ data = 0;
+
+ if (_iconURL.length() < 1) {
+ LOG_ERROR("Attempt to set icon for blank url");
+ return;
+ }
+
+ String iconURL = _iconURL;
+ iconURL.replace('\'', "''");
+
+ // If we're in private browsing, we'll keep a record in cache instead of in the DB
+ if (m_privateBrowsingEnabled) {
+ // FIXME - set data in the temporary tables
+ return;
+ }
+
+ int64_t iconID = establishIconIDForEscapedIconURL(iconURL);
+ if (iconID) {
+ // The following statement works to set the icon data to NULL because sqlite defaults unbound ? parameters to null
+ SQLStatement sql(m_db, "UPDATE IconResource SET data = ? WHERE iconID = ?;");
+ sql.prepare();
+ if (data)
+ sql.bindBlob(1, data, size);
+ sql.bindInt64(2, iconID);
+ if (sql.step() != SQLITE_DONE)
+ LOG_ERROR("Unable to set icon resource data for iconID %i", iconID);
+ }
}
-void IconDatabase::setHaveNoIconForIconURL(const String& iconURL)
+int IconDatabase::establishIconIDForEscapedIconURL(const String& iconURL)
{
+ int64_t iconID = 0;
+ SQLStatement sql(m_db, "SELECT iconID FROM Icon WHERE url = '" + iconURL + "';");
+ sql.prepare();
+ if (sql.step() == SQLITE_ROW) {
+ iconID = sql.getColumnInt64(0);
+ } else {
+ sql.finalize();
+ if (m_db.executeCommand("INSERT INTO Icon (url) VALUES ('" + iconURL + "');"))
+ iconID = m_db.lastInsertRowID();
+ }
+ return iconID;
+}
+void IconDatabase::setHaveNoIconForIconURL(const String& _iconURL)
+{
+ String iconURL = _iconURL;
+ iconURL.replace('\'', "''");
+
+ // If we're in private browsing, we'll keep a record in cache instead of in the DB
+ if (m_privateBrowsingEnabled) {
+ // FIXME - set data in the temporary tables
+ return;
+ }
+
+ int64_t iconID = establishIconIDForEscapedIconURL(iconURL);
+ if (iconID) {
+ // The following statement works to set the icon data to NULL because sqlite defaults unbound ? parameters to null
+ SQLStatement sql(m_db, "UPDATE IconResource SET data = ? WHERE iconID = ?;");
+ sql.prepare();
+ sql.bindInt64(2, iconID);
+ if (sql.step() != SQLITE_DONE)
+ LOG_ERROR("Unable to set icon resource data for iconID %i", iconID);
+ }
}
-void IconDatabase::setIconURLForPageURL(const String& iconURL, const String& pageURL)
+void IconDatabase::setIconURLForPageURL(const String& _iconURL, const String& _pageURL)
{
-
+ String iconURL = _iconURL;
+ iconURL.replace('\'',"''");
+ String pageURL = _pageURL;
+ pageURL.replace('\'',"''");
+
+ // If we're in private browsing, we'll keep a record in a temporary table
+ if (m_privateBrowsingEnabled) {
+ // FIXME - set data in the temporary tables
+ return;
+ }
+
+ // Check if an entry in the Icon table already exists for this iconURL
+ // Get this iconID, or create a new one
+ int64_t iconID = 0;
+ SQLStatement sql(m_db, "SELECT iconID FROM Icon WHERE url = '" + iconURL + "';");
+ sql.prepare();
+ if (sql.step() == SQLITE_ROW) {
+ iconID = sql.getColumnInt64(0);
+ sql.finalize();
+ } else {
+ sql.finalize();
+ if (m_db.executeCommand("INSERT INTO Icon (url) VALUES ('" + iconURL + "');"))
+ iconID = m_db.lastInsertRowID();
+ }
+
+ if (!iconID) {
+ LOG_ERROR("Unable to establish iconID for iconURL %s - %s", iconURL.ascii().data(), m_db.lastErrorMsg());
+ return;
+ }
+
+ if (m_db.returnsAtLeastOneResult("SELECT url FROM PageURL WHERE url = '" + pageURL + "';")) {
+ if (!m_db.executeCommand("UPDATE PageURL SET iconID = " + String::number(iconID) + " WHERE url = '" + pageURL + "';"))
+ LOG_ERROR("Failed to update record in PageURL table - %s", m_db.lastErrorMsg());
+ } else {
+ if( !m_db.executeCommand("INSERT INTO PageURL (url, iconID) VALUES ('" + pageURL + "', " + String::number(iconID) + ");"))
+ LOG_ERROR("Failed to insert record into PageURL - %s", m_db.lastErrorMsg());
+ }
}
-bool IconDatabase::hasIconForIconURL(const String& url)
+bool IconDatabase::hasIconForIconURL(const String& _url)
{
- return false;
+ // Places to check -
+ // -iconURLToSiteIcon map
+ // -On disk SQL
+ // -Temporary SQL if private browsing enabled
+
+ String url = _url;
+ url.replace('\'', "''");
+
+ // If we're in private browsing, we'll check the temporary table as a backup
+ if (m_privateBrowsingEnabled) {
+ // FIXME - make this AFTER the primary checks as we can probably safely consider private browsing to be less common
+ // than regular
+ return false;
+ }
+
+ String query = "SELECT IconResource.data FROM IconResource, Icon WHERE Icon.url = '" + url + "' AND IconResource.iconID = Icon.iconID;";
+ int size;
+ const void* data = SQLStatement(m_db, query).getColumnBlob(0, size);
+ return (data && size);
}
IconDatabase::~IconDatabase()
#include "IntSize.h"
#include "PlatformString.h"
#include "SQLDatabase.h"
+#include "StringHash.h"
#include <wtf/HashMap.h>
+namespace WTF {
+
+template<> struct IntHash<IntSize> {
+ static unsigned hash(const IntSize& key) { return intHash(((uint64_t)(key.width()) << 32 | key.height())); }
+ static bool equal(const IntSize& a, const IntSize& b) { return a.width() == b.width() && a.height() == b.height(); }
+};
+template<> struct DefaultHash<IntSize> { typedef IntHash<IntSize> Hash; };
+
+} //namespace WTF
+
namespace WebCore {
class Image;
-
-class WebIcon {
+
+class SiteIcon {
public:
- WebIcon();
- ~WebIcon();
+ SiteIcon(const String& url);
+ ~SiteIcon();
void resetExpiration(time_t newExpiration = 0);
- time_t getExpiration() { return m_expire; }
-
+ time_t getExpiration();
+
//getImage() inherently touch()es the icon
- Image* getImage();
+ Image* getImage(const IntSize&);
//incase a user wants to manually touch() the icon
void touch();
time_t getTouch() { return m_touch; }
+
+ String getIconURL() { return m_iconURL; }
private:
+ String m_iconURL;
time_t m_touch;
time_t m_expire;
Image* m_image;
+
+ // 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;
};
class IconDatabase
{
-//TODO - WebIcon is never to be used outside of IconDatabase
-friend class WebIcon;
+//TODO - SiteIcon is never to be used outside of IconDatabase, so make it an internal and remove the friendness
+friend class SiteIcon;
public:
static IconDatabase* sharedIconDatabase();
bool isOpen() { return m_db.isOpen(); }
void close();
- Image* iconForURL(const String&, const IntSize&, bool cache = true);
+ Image* iconForPageURL(const String&, const IntSize&, bool cache = true);
+ Image* iconForIconURL(const String&, const IntSize&, bool cache = true);
String iconURLForURL(const String&);
Image* defaultIcon(const IntSize&);
void releaseIconForURL(const String&);
void setPrivateBrowsingEnabled(bool flag);
+ bool getPrivateBrowsingEnabled() { return m_privateBrowsingEnabled; }
bool hasIconForIconURL(const String&);
- //TODO - The following 3 methods were considered private in WebKit - analyze the impact of making them
- //public here in WebCore - I don't see any real badness with doing that... afterall if Chuck Norris wants to muck
- //around with the icons in his database, he's going to anyway
- void setIconForIconURL(Image*, const String&);
+ // TODO - The following 3 methods were considered private in WebKit - analyze the impact of making them
+ // public here in WebCore - I don't see any real badness with doing that... after all if Chuck Norris wants to muck
+ // around with the icons in his database, he's going to anyway
+ void setIconDataForIconURL(const void* data, int size, const String&);
void setHaveNoIconForIconURL(const String&);
void setIconURLForPageURL(const String& iconURL, const String& pageURL);
bool isValidDatabase();
void clearDatabase();
void recreateDatabase();
+
+ void createPrivateTables();
+ void deletePrivateTables();
+
+ int establishIconIDForEscapedIconURL(const String&);
+
+ // The following three methods follow the sqlite convention for blob data
+ // They return a const void* which is a pointer to the data buffer, and store
+ // the size of the data buffer in the int& parameter
+ const void* imageDataForIconID(int, int&);
+ const void* imageDataForIconURL(const String&, int&);
+ const void* imageDataForPageURL(const String&, int&);
static IconDatabase* m_sharedInstance;
static const int DefaultCachedPageCount;
SQLDatabase m_db;
bool m_privateBrowsingEnabled;
- typedef WTF::HashMap<String, WebIcon*> WebIconMap;
- WebIconMap m_webIcons;
+ typedef HashMap<String, SiteIcon*> SiteIconMap;
+ SiteIconMap m_pageURLToSiteIcons;
+ SiteIconMap m_iconURLToSiteIcons;
};
} //namespace WebCore
--- /dev/null
+/*
+ * 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 "IconDatabase.h"
+
+#include "Logging.h"
+#include "Image.h"
+#include <limits.h>
+
+using namespace WebCore;
+
+SiteIcon::SiteIcon(const String& url)
+ : m_iconURL(url)
+ , m_touch(0)
+ , m_image(0)
+{
+
+}
+
+SiteIcon::~SiteIcon()
+{
+
+}
+
+Image* SiteIcon::getImage(const IntSize& size)
+{
+ // FIXME - For size right now, we are resizing our one-and-only shared Image
+ // What we really need to do is keep a hashmap of Images based on size and make a copy when/if we create a new one
+ if (m_image)
+ return m_image;
+
+ if (IconDatabase::m_sharedInstance) {
+ int size;
+ const void* imageData = IconDatabase::m_sharedInstance->imageDataForIconURL(m_iconURL, size);
+ if (!imageData || !size)
+ return 0;
+ NativeBytePtr nativeData = 0;
+ // FIXME - Any other platform will need their own method to create NativeBytePtr from the void*
+#ifdef __APPLE__
+ nativeData = CFDataCreate(NULL, (const UInt8*)imageData, size);
+#endif
+ m_image = new Image();
+ if (m_image->setNativeData(nativeData, true))
+ return m_image;
+ delete m_image;
+ return m_image = 0;
+ }
+ return 0;
+}
+
+void SiteIcon::resetExpiration(time_t newExpiration)
+{
+ // FIXME - Write expiration time to SQL
+}
+
+time_t SiteIcon::getExpiration()
+{
+ // FIXME - Return expiration time from SQL
+ return INT_MAX;
+}
+
+//void touch();
+
+2006-06-27 Brady Eidson <beidson@apple.com>
+
+ Reviewed by Maciej
+
+ Hookup the new semi-functional SQLite icon database.
+ For now, it is living side-by-side with the old DB so one can compare the
+ two for debugging purposes. Also, it is disabled (in WebKit) by default unless you
+ compile with ICONDEBUG #defined.
+ Note: To repeat that, if you want to try the new DB, #define ICONDEBUG (WebKitPrefix.h is a good place to do it)
+
+ * Misc/WebIconDatabase.m:
+ (-[NSMutableDictionary iconForURL:withSize:cache:]):
+ (-[NSMutableDictionary iconURLForURL:]):
+ (-[NSMutableDictionary retainIconForURL:]):
+ (-[NSMutableDictionary releaseIconForURL:]):
+ (-[WebIconDatabase _setHaveNoIconForIconURL:]):
+ (-[WebIconDatabase _setIconURL:forURL:]):
+ (-[WebIconDatabase _hasIconForIconURL:]):
+ (-[WebIconDatabase _resetCachedWebPreferences:]):
+ * Misc/WebIconLoader.m:
+ (-[WebIconLoader didFinishLoading]):
+ * WebKit.xcodeproj/project.pbxproj:
+
2006-06-26 David Hyatt <hyatt@apple.com>
Fix for 9538, support syntax highlighting for HTML source.
if ([URL _webkit_isFileURL])
return [self _iconForFileURL:URL withSize:size];
-
- NSString *iconURLString = [_private->pageURLToIconURL objectForKey:URL];
- if (!iconURLString)
- // Don't have it
- return [self defaultIconWithSize:size];
- NSMutableDictionary *icons = [self _iconsForIconURLString:iconURLString];
-#ifdef ICONDEBUG
- NSImage* image = [_private->databaseBridge iconForURL:URL withSize:size];
+#ifdef ICONDEBUG
+ NSImage* image = [_private->databaseBridge iconForPageURL:URL withSize:size];
if (image)
LOG(IconDatabase, "NewDB has image for %@", URL);
else
LOG(IconDatabase, "NewDB has no image for %@", URL);
- if (icons)
- LOG(IconDatabase, "OldDB has at least 1 image for %@", URL);
- else
- LOG(IconDatabase, "OldDB has no image for %@", URL);
+
+ // FIXME - We currently don't embed the default icon in the new WebCore IconDB, so we'll return the old version of it;
+ return image ? image : [self defaultIconWithSize:size];
#endif
+ NSString *iconURLString = [_private->pageURLToIconURL objectForKey:URL];
+ if (!iconURLString)
+ // Don't have it
+ return [self defaultIconWithSize:size];
+
+ NSMutableDictionary *icons = [self _iconsForIconURLString:iconURLString];
+
if (!icons) {
if (![_private->iconURLsWithNoIcons containsObject:iconURLString]) {
// We used to have this icon, but don't have it anymore for some reason. (Bug? Deleted from
if (![self _isEnabled])
return nil;
#ifdef ICONDEBUG
- NSString* iconurl = [_private->databaseBridge iconURLForURL:URL];
- LOG(IconDatabase, "NewDB has IconURL %@ for PageURL %@", iconurl, URL);
- LOG(IconDatabase, "OldDB has IconURL %@ for PageURL %@", [_private->pageURLToIconURL objectForKey:URL], URL);
+ NSString* iconurl = [_private->databaseBridge iconURLForPageURL:URL];
+ NSString* oldURL = [_private->pageURLToIconURL objectForKey:URL];
+ if ([oldURL isEqualToString:iconurl])
+ LOG(IconDatabase,"iconURLForURL - Old and New icon databases agree on %@ for PageURL %@", iconurl, URL);
+ else
+ LOG(IconDatabase,"iconURLForURL - Old (%@) and New (%@) disagree on PageURL %@", oldURL, iconurl, URL );
#endif
return URL ? [_private->pageURLToIconURL objectForKey:URL] : nil;
}
CFDictionarySetValue(_private->pageURLToRetainCount, URL, (void *)(retainCount + 1));
#ifdef ICONDEBUG
[_private->databaseBridge retainIconForURL:URL];
- LOG(IconDatabase, "NewDB - Icon retained for URL %@", URL);
#endif
}
return;
#ifdef ICONDEBUG
[_private->databaseBridge releaseIconForURL:pageURL];
- LOG(IconDatabase, "NewDB - Icon released for URL %@", pageURL);
#endif
WebNSUInteger retainCount = (WebNSUInteger)(void *)CFDictionaryGetValue(_private->pageURLToRetainCount, pageURL);
#ifdef ICONDEBUG
[_private->databaseBridge _setHaveNoIconForIconURL:iconURL];
- LOG(IconDatabase, "NewDB - Set haveNoIcon for IconURL %@", iconURL);
#endif
[_private->iconURLsWithNoIcons addObject:iconURL];
ASSERT(iconURL);
ASSERT(URL);
ASSERT([self _isEnabled]);
- ASSERT([self _hasIconForIconURL:iconURL]);
ASSERT(_private->pageURLToIconURL);
#ifdef ICONDEBUG
[_private->databaseBridge _setIconURL:iconURL forURL:URL];
- LOG(IconDatabase, "NewDB - Icon URL %@ set for URL %@", iconURL, URL);
#endif
if ([[_private->pageURLToIconURL objectForKey:URL] isEqualToString:iconURL]) {
#ifdef ICONDEBUG
BOOL result = [_private->databaseBridge _hasIconForIconURL:iconURL];
if (result)
- LOG(IconDatabase, "NewDB - Has icon for IconURL %@", iconURL);
- else
- LOG(IconDatabase, "NewDB - Does NOT have icon for IconURL %@", iconURL);
- if (([_private->iconURLToIcons objectForKey:iconURL] ||
- [_private->iconURLsWithNoIcons containsObject:iconURL] ||
- [_private->iconsOnDiskWithURLs containsObject:iconURL]) &&
- [self _totalRetainCountForIconURLString:iconURL] > 0)
- LOG(IconDatabase, "OldDB - Has icon for IconURL %@", iconURL);
+ LOG(IconDatabase, "NewDB has icon for IconURL %@", iconURL);
else
- LOG(IconDatabase, "NewDB - Does NOT have icon for IconURL %@", iconURL);
+ LOG(IconDatabase, "NewDB has NO icon for IconURL %@", iconURL);
+ return result;
#endif
return (([_private->iconURLToIcons objectForKey:iconURL] ||
return;
_private->privateBrowsingEnabled = privateBrowsingEnabledNow;
+#ifdef ICONDEBUG
+ [_private->databaseBridge setPrivateBrowsingEnabled:privateBrowsingEnabledNow];
+#endif
// When private browsing is turned off, forget everything we learned while it was on
if (!_private->privateBrowsingEnabled) {
- (void)didFinishLoading
{
NSImage *icon;
-
- #ifdef ICONDEBUG
- NSData *_data = [self resourceData];
- if (_data) {
- [[WebCoreIconDatabaseBridge sharedBridgeInstance] _setIconData:_data forIconURL:[[self URL] _web_originalDataAsString]];
- LOG(IconDatabase, "NewDB - Icon data set for URL %@", [[self URL] _web_originalDataAsString]);
- }
- #endif
-
+
+
NS_DURING
NSData *data = [self resourceData];
+
+ #ifdef ICONDEBUG
+ if (data) {
+ [[WebCoreIconDatabaseBridge sharedBridgeInstance] _setIconData:data forIconURL:[[self URL] _web_originalDataAsString]];
+ LOG(IconDatabase, "NewDB - Icon data set for URL %@", [[self URL] _web_originalDataAsString]);
+ }
+ #endif
+
icon = [data length] > 0 ? [[NSImage alloc] initWithData:data] : nil;
NS_HANDLER
icon = nil;
ED7F6D8B0980683500C235ED /* WebNSDataExtrasPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7F6D8A0980683500C235ED /* WebNSDataExtrasPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
/* End PBXBuildFile section */
-/* Begin PBXBuildStyle section */
- BCCD729D0A4A60FF005FDA6D /* Development */ = {
- isa = PBXBuildStyle;
- buildSettings = {
- COPY_PHASE_STRIP = NO;
- };
- name = Development;
- };
- BCCD729E0A4A60FF005FDA6D /* Deployment */ = {
- isa = PBXBuildStyle;
- buildSettings = {
- COPY_PHASE_STRIP = YES;
- };
- name = Deployment;
- };
-/* End PBXBuildStyle section */
-
/* Begin PBXFileReference section */
1C0706620A431E01001078F6 /* WebScriptDebugServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebScriptDebugServer.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
1C07073C0A433BD8001078F6 /* WebScriptDebugServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebScriptDebugServer.m; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
0867D690FE84028FC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 149C283208902B0F008A9EFC /* Build configuration list for PBXProject "WebKit" */;
- buildSettings = {
- };
- buildStyles = (
- BCCD729D0A4A60FF005FDA6D /* Development */,
- BCCD729E0A4A60FF005FDA6D /* Deployment */,
- );
hasScannedForEncodings = 1;
knownRegions = (
English,