WebKit/mac/Misc/WebIconDatabase.mm
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
       
     3  *
       
     4  * Redistribution and use in source and binary forms, with or without
       
     5  * modification, are permitted provided that the following conditions
       
     6  * are met:
       
     7  *
       
     8  * 1.  Redistributions of source code must retain the above copyright
       
     9  *     notice, this list of conditions and the following disclaimer. 
       
    10  * 2.  Redistributions in binary form must reproduce the above copyright
       
    11  *     notice, this list of conditions and the following disclaimer in the
       
    12  *     documentation and/or other materials provided with the distribution. 
       
    13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
       
    14  *     its contributors may be used to endorse or promote products derived
       
    15  *     from this software without specific prior written permission. 
       
    16  *
       
    17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
       
    18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
    19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
       
    20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
       
    21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
       
    22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
       
    23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
       
    24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
       
    26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    27  */
       
    28 
       
    29 #import "WebIconDatabaseInternal.h"
       
    30 
       
    31 #import "WebIconDatabaseClient.h"
       
    32 #import "WebIconDatabaseDelegate.h"
       
    33 #import "WebKitLogging.h"
       
    34 #import "WebKitNSStringExtras.h"
       
    35 #import "WebNSFileManagerExtras.h"
       
    36 #import "WebNSNotificationCenterExtras.h"
       
    37 #import "WebNSURLExtras.h"
       
    38 #import "WebPreferences.h"
       
    39 #import "WebTypesInternal.h"
       
    40 #import <WebCore/IconDatabase.h>
       
    41 #import <WebCore/Image.h>
       
    42 #import <WebCore/IntSize.h>
       
    43 #import <WebCore/SharedBuffer.h>
       
    44 #import <WebCore/ThreadCheck.h>
       
    45 #import <runtime/InitializeThreading.h>
       
    46 #import <wtf/Threading.h>
       
    47 
       
    48 using namespace WebCore;
       
    49 
       
    50 NSString * const WebIconDatabaseVersionKey =    @"WebIconDatabaseVersion";
       
    51 NSString * const WebURLToIconURLKey =           @"WebSiteURLToIconURLKey";
       
    52 
       
    53 NSString *WebIconDatabaseDidAddIconNotification =          @"WebIconDatabaseDidAddIconNotification";
       
    54 NSString *WebIconNotificationUserInfoURLKey =              @"WebIconNotificationUserInfoURLKey";
       
    55 NSString *WebIconDatabaseDidRemoveAllIconsNotification =   @"WebIconDatabaseDidRemoveAllIconsNotification";
       
    56 
       
    57 NSString *WebIconDatabaseDirectoryDefaultsKey = @"WebIconDatabaseDirectoryDefaultsKey";
       
    58 NSString *WebIconDatabaseImportDirectoryDefaultsKey = @"WebIconDatabaseImportDirectoryDefaultsKey";
       
    59 NSString *WebIconDatabaseEnabledDefaultsKey =   @"WebIconDatabaseEnabled";
       
    60 
       
    61 NSString *WebIconDatabasePath = @"~/Library/Icons";
       
    62 
       
    63 NSSize WebIconSmallSize = {16, 16};
       
    64 NSSize WebIconMediumSize = {32, 32};
       
    65 NSSize WebIconLargeSize = {128, 128};
       
    66 
       
    67 #define UniqueFilePathSize (34)
       
    68 
       
    69 static WebIconDatabaseClient* defaultClient()
       
    70 {
       
    71 #if ENABLE(ICONDATABASE)
       
    72     static WebIconDatabaseClient* defaultClient = new WebIconDatabaseClient();
       
    73     return defaultClient;
       
    74 #else
       
    75     return 0;
       
    76 #endif
       
    77 }
       
    78 
       
    79 @interface WebIconDatabase (WebReallyInternal)
       
    80 - (void)_sendNotificationForURL:(NSString *)URL;
       
    81 - (void)_sendDidRemoveAllIconsNotification;
       
    82 - (NSImage *)_iconForFileURL:(NSString *)fileURL withSize:(NSSize)size;
       
    83 - (void)_resetCachedWebPreferences:(NSNotification *)notification;
       
    84 - (NSImage *)_largestIconFromDictionary:(NSMutableDictionary *)icons;
       
    85 - (NSMutableDictionary *)_iconsBySplittingRepresentationsOfIcon:(NSImage *)icon;
       
    86 - (NSImage *)_iconFromDictionary:(NSMutableDictionary *)icons forSize:(NSSize)size cache:(BOOL)cache;
       
    87 - (void)_scaleIcon:(NSImage *)icon toSize:(NSSize)size;
       
    88 - (NSString *)_databaseDirectory;
       
    89 @end
       
    90 
       
    91 @implementation WebIconDatabase
       
    92 
       
    93 + (void)initialize
       
    94 {
       
    95     JSC::initializeThreading();
       
    96     WTF::initializeMainThreadToProcessMainThread();
       
    97 }
       
    98 
       
    99 + (WebIconDatabase *)sharedIconDatabase
       
   100 {
       
   101     static WebIconDatabase *database = nil;
       
   102     if (!database)
       
   103         database = [[WebIconDatabase alloc] init];
       
   104     return database;
       
   105 }
       
   106 
       
   107 - (id)init
       
   108 {
       
   109     [super init];
       
   110     WebCoreThreadViolationCheckRoundOne();
       
   111         
       
   112     _private = [[WebIconDatabasePrivate alloc] init];
       
   113     
       
   114     // Check the user defaults and see if the icon database should even be enabled.
       
   115     // Inform the bridge and, if we're disabled, bail from init right here
       
   116     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
       
   117     // <rdar://problem/4741419> - IconDatabase should be disabled by default
       
   118     NSDictionary *initialDefaults = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithBool:YES], WebIconDatabaseEnabledDefaultsKey, nil];
       
   119     [defaults registerDefaults:initialDefaults];
       
   120     [initialDefaults release];
       
   121     BOOL enabled = [defaults boolForKey:WebIconDatabaseEnabledDefaultsKey];
       
   122     iconDatabase()->setEnabled(enabled);
       
   123     if (enabled)
       
   124         [self _startUpIconDatabase];
       
   125     return self;
       
   126 }
       
   127 
       
   128 - (NSImage *)iconForURL:(NSString *)URL withSize:(NSSize)size cache:(BOOL)cache
       
   129 {
       
   130     ASSERT_MAIN_THREAD();
       
   131     ASSERT(size.width);
       
   132     ASSERT(size.height);
       
   133 
       
   134     if (!URL || ![self isEnabled])
       
   135         return [self defaultIconForURL:URL withSize:size];
       
   136 
       
   137     // FIXME - <rdar://problem/4697934> - Move the handling of FileURLs to WebCore and implement in ObjC++
       
   138     if ([URL _webkit_isFileURL])
       
   139         return [self _iconForFileURL:URL withSize:size];
       
   140       
       
   141     if (Image* image = iconDatabase()->iconForPageURL(URL, IntSize(size)))
       
   142         if (NSImage *icon = webGetNSImage(image, size))
       
   143             return icon;
       
   144     return [self defaultIconForURL:URL withSize:size];
       
   145 }
       
   146 
       
   147 - (NSImage *)iconForURL:(NSString *)URL withSize:(NSSize)size
       
   148 {
       
   149     return [self iconForURL:URL withSize:size cache:YES];
       
   150 }
       
   151 
       
   152 - (NSString *)iconURLForURL:(NSString *)URL
       
   153 {
       
   154     if (![self isEnabled])
       
   155         return nil;
       
   156     ASSERT_MAIN_THREAD();
       
   157 
       
   158     return iconDatabase()->iconURLForPageURL(URL);
       
   159 }
       
   160 
       
   161 - (NSImage *)defaultIconWithSize:(NSSize)size
       
   162 {
       
   163     ASSERT_MAIN_THREAD();
       
   164     ASSERT(size.width);
       
   165     ASSERT(size.height);
       
   166     
       
   167     Image* image = iconDatabase()->defaultIcon(IntSize(size));
       
   168     return image ? image->getNSImage() : nil;
       
   169 }
       
   170 
       
   171 - (NSImage *)defaultIconForURL:(NSString *)URL withSize:(NSSize)size
       
   172 {
       
   173     if (_private->delegateImplementsDefaultIconForURL)
       
   174         return [_private->delegate webIconDatabase:self defaultIconForURL:URL withSize:size];
       
   175     return [self defaultIconWithSize:size];
       
   176 }
       
   177 
       
   178 - (void)retainIconForURL:(NSString *)URL
       
   179 {
       
   180     ASSERT_MAIN_THREAD();
       
   181     ASSERT(URL);
       
   182     if (![self isEnabled])
       
   183         return;
       
   184 
       
   185     iconDatabase()->retainIconForPageURL(URL);
       
   186 }
       
   187 
       
   188 - (void)releaseIconForURL:(NSString *)pageURL
       
   189 {
       
   190     ASSERT_MAIN_THREAD();
       
   191     ASSERT(pageURL);
       
   192     if (![self isEnabled])
       
   193         return;
       
   194 
       
   195     iconDatabase()->releaseIconForPageURL(pageURL);
       
   196 }
       
   197 
       
   198 + (void)delayDatabaseCleanup
       
   199 {
       
   200     ASSERT_MAIN_THREAD();
       
   201 
       
   202     IconDatabase::delayDatabaseCleanup();
       
   203 }
       
   204 
       
   205 + (void)allowDatabaseCleanup
       
   206 {
       
   207     ASSERT_MAIN_THREAD();
       
   208 
       
   209     IconDatabase::allowDatabaseCleanup();
       
   210 }
       
   211 
       
   212 - (void)setDelegate:(id)delegate
       
   213 {
       
   214     _private->delegate = delegate;
       
   215     _private->delegateImplementsDefaultIconForURL = [delegate respondsToSelector:@selector(webIconDatabase:defaultIconForURL:withSize:)];
       
   216 }
       
   217 
       
   218 - (id)delegate
       
   219 {
       
   220     return _private->delegate;
       
   221 }
       
   222 
       
   223 @end
       
   224 
       
   225 
       
   226 @implementation WebIconDatabase (WebPendingPublic)
       
   227 
       
   228 - (BOOL)isEnabled
       
   229 {
       
   230     return iconDatabase()->isEnabled();
       
   231 }
       
   232 
       
   233 - (void)setEnabled:(BOOL)flag
       
   234 {
       
   235     BOOL currentlyEnabled = [self isEnabled];
       
   236     if (currentlyEnabled && !flag) {
       
   237         iconDatabase()->setEnabled(false);
       
   238         [self _shutDownIconDatabase];
       
   239     } else if (!currentlyEnabled && flag) {
       
   240         iconDatabase()->setEnabled(true);
       
   241         [self _startUpIconDatabase];
       
   242     }
       
   243 }
       
   244 
       
   245 - (void)removeAllIcons
       
   246 {
       
   247     ASSERT_MAIN_THREAD();
       
   248     if (![self isEnabled])
       
   249         return;
       
   250 
       
   251     // Via the IconDatabaseClient interface, removeAllIcons() will send the WebIconDatabaseDidRemoveAllIconsNotification
       
   252     iconDatabase()->removeAllIcons();
       
   253 }
       
   254 
       
   255 @end
       
   256 
       
   257 @implementation WebIconDatabase (WebPrivate)
       
   258 
       
   259 + (void)_checkIntegrityBeforeOpening
       
   260 {
       
   261     iconDatabase()->checkIntegrityBeforeOpening();
       
   262 }
       
   263 
       
   264 @end
       
   265 
       
   266 @implementation WebIconDatabase (WebInternal)
       
   267 
       
   268 - (void)_sendNotificationForURL:(NSString *)URL
       
   269 {
       
   270     ASSERT(URL);
       
   271     
       
   272     NSDictionary *userInfo = [NSDictionary dictionaryWithObject:URL
       
   273                                                          forKey:WebIconNotificationUserInfoURLKey];
       
   274                                                          
       
   275     [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:WebIconDatabaseDidAddIconNotification
       
   276                                                         object:self
       
   277                                                       userInfo:userInfo];
       
   278 }
       
   279 
       
   280 - (void)_sendDidRemoveAllIconsNotification
       
   281 {
       
   282     [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:WebIconDatabaseDidRemoveAllIconsNotification
       
   283                                                         object:self
       
   284                                                       userInfo:nil];
       
   285 }
       
   286 
       
   287 - (void)_startUpIconDatabase
       
   288 {
       
   289     iconDatabase()->setClient(defaultClient());
       
   290     
       
   291     // Figure out the directory we should be using for the icon.db
       
   292     NSString *databaseDirectory = [self _databaseDirectory];
       
   293     
       
   294     // Rename legacy icon database files to the new icon database name
       
   295     BOOL isDirectory = NO;
       
   296     NSString *legacyDB = [databaseDirectory stringByAppendingPathComponent:@"icon.db"];
       
   297     NSFileManager *defaultManager = [NSFileManager defaultManager];
       
   298     if ([defaultManager fileExistsAtPath:legacyDB isDirectory:&isDirectory] && !isDirectory) {
       
   299         NSString *newDB = [databaseDirectory stringByAppendingPathComponent:iconDatabase()->defaultDatabaseFilename()];
       
   300         if (![defaultManager fileExistsAtPath:newDB])
       
   301             rename([legacyDB fileSystemRepresentation], [newDB fileSystemRepresentation]);
       
   302     }
       
   303     
       
   304     // Set the private browsing pref then open the WebCore icon database
       
   305     iconDatabase()->setPrivateBrowsingEnabled([[WebPreferences standardPreferences] privateBrowsingEnabled]);
       
   306     if (!iconDatabase()->open(databaseDirectory))
       
   307         LOG_ERROR("Unable to open icon database");
       
   308     
       
   309     // Register for important notifications
       
   310     [[NSNotificationCenter defaultCenter] addObserver:self
       
   311                                              selector:@selector(_applicationWillTerminate:)
       
   312                                                  name:NSApplicationWillTerminateNotification
       
   313                                                object:NSApp];
       
   314     [[NSNotificationCenter defaultCenter] addObserver:self
       
   315                                              selector:@selector(_resetCachedWebPreferences:)
       
   316                                                  name:WebPreferencesChangedNotification
       
   317                                                object:nil];
       
   318 }
       
   319 
       
   320 - (void)_shutDownIconDatabase
       
   321 {
       
   322     // Unregister for important notifications
       
   323     [[NSNotificationCenter defaultCenter] removeObserver:self
       
   324                                                     name:NSApplicationWillTerminateNotification
       
   325                                                   object:NSApp];
       
   326     [[NSNotificationCenter defaultCenter] removeObserver:self
       
   327                                                     name:WebPreferencesChangedNotification
       
   328                                                   object:nil];
       
   329 }
       
   330 
       
   331 - (void)_applicationWillTerminate:(NSNotification *)notification
       
   332 {
       
   333     iconDatabase()->close();
       
   334 }
       
   335 
       
   336 - (NSImage *)_iconForFileURL:(NSString *)file withSize:(NSSize)size
       
   337 {
       
   338     ASSERT_MAIN_THREAD();
       
   339     ASSERT(size.width);
       
   340     ASSERT(size.height);
       
   341 
       
   342     NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
       
   343     NSString *path = [[NSURL _web_URLWithDataAsString:file] path];
       
   344     NSString *suffix = [path pathExtension];
       
   345     NSImage *icon = nil;
       
   346     
       
   347     if ([suffix _webkit_isCaseInsensitiveEqualToString:@"htm"] || [suffix _webkit_isCaseInsensitiveEqualToString:@"html"]) {
       
   348         if (!_private->htmlIcons) {
       
   349             icon = [workspace iconForFileType:@"html"];
       
   350             _private->htmlIcons = [[self _iconsBySplittingRepresentationsOfIcon:icon] retain];
       
   351         }
       
   352         icon = [self _iconFromDictionary:_private->htmlIcons forSize:size cache:YES];
       
   353     } else {
       
   354         if (!path || ![path isAbsolutePath]) {
       
   355             // Return the generic icon when there is no path.
       
   356             icon = [workspace iconForFileType:NSFileTypeForHFSTypeCode(kGenericDocumentIcon)];
       
   357         } else {
       
   358             icon = [workspace iconForFile:path];
       
   359         }
       
   360         [self _scaleIcon:icon toSize:size];
       
   361     }
       
   362 
       
   363     return icon;
       
   364 }
       
   365 
       
   366 - (void)_resetCachedWebPreferences:(NSNotification *)notification
       
   367 {
       
   368     BOOL privateBrowsingEnabledNow = [[WebPreferences standardPreferences] privateBrowsingEnabled];
       
   369     iconDatabase()->setPrivateBrowsingEnabled(privateBrowsingEnabledNow);
       
   370 }
       
   371 
       
   372 - (NSImage *)_largestIconFromDictionary:(NSMutableDictionary *)icons
       
   373 {
       
   374     ASSERT(icons);
       
   375     
       
   376     NSEnumerator *enumerator = [icons keyEnumerator];
       
   377     NSValue *currentSize, *largestSize=nil;
       
   378     float largestSizeArea=0;
       
   379 
       
   380     while ((currentSize = [enumerator nextObject]) != nil) {
       
   381         NSSize currentSizeSize = [currentSize sizeValue];
       
   382         float currentSizeArea = currentSizeSize.width * currentSizeSize.height;
       
   383         if(!largestSizeArea || (currentSizeArea > largestSizeArea)){
       
   384             largestSize = currentSize;
       
   385             largestSizeArea = currentSizeArea;
       
   386         }
       
   387     }
       
   388 
       
   389     return [icons objectForKey:largestSize];
       
   390 }
       
   391 
       
   392 - (NSMutableDictionary *)_iconsBySplittingRepresentationsOfIcon:(NSImage *)icon
       
   393 {
       
   394     ASSERT(icon);
       
   395 
       
   396     NSMutableDictionary *icons = [NSMutableDictionary dictionary];
       
   397     NSEnumerator *enumerator = [[icon representations] objectEnumerator];
       
   398     NSImageRep *rep;
       
   399 
       
   400     while ((rep = [enumerator nextObject]) != nil) {
       
   401         NSSize size = [rep size];
       
   402         NSImage *subIcon = [[NSImage alloc] initWithSize:size];
       
   403         [subIcon addRepresentation:rep];
       
   404         [icons setObject:subIcon forKey:[NSValue valueWithSize:size]];
       
   405         [subIcon release];
       
   406     }
       
   407 
       
   408     if([icons count] > 0)
       
   409         return icons;
       
   410 
       
   411     LOG_ERROR("icon has no representations");
       
   412     
       
   413     return nil;
       
   414 }
       
   415 
       
   416 - (NSImage *)_iconFromDictionary:(NSMutableDictionary *)icons forSize:(NSSize)size cache:(BOOL)cache
       
   417 {
       
   418     ASSERT(size.width);
       
   419     ASSERT(size.height);
       
   420 
       
   421     NSImage *icon = [icons objectForKey:[NSValue valueWithSize:size]];
       
   422 
       
   423     if(!icon){
       
   424         icon = [[[self _largestIconFromDictionary:icons] copy] autorelease];
       
   425         [self _scaleIcon:icon toSize:size];
       
   426 
       
   427         if(cache){
       
   428             [icons setObject:icon forKey:[NSValue valueWithSize:size]];
       
   429         }
       
   430     }
       
   431 
       
   432     return icon;
       
   433 }
       
   434 
       
   435 - (void)_scaleIcon:(NSImage *)icon toSize:(NSSize)size
       
   436 {
       
   437     ASSERT(size.width);
       
   438     ASSERT(size.height);
       
   439     
       
   440 #if !LOG_DISABLED        
       
   441     double start = CFAbsoluteTimeGetCurrent();
       
   442 #endif
       
   443     
       
   444     [icon setScalesWhenResized:YES];
       
   445     [icon setSize:size];
       
   446     
       
   447 #if !LOG_DISABLED
       
   448     double duration = CFAbsoluteTimeGetCurrent() - start;
       
   449     LOG(Timing, "scaling icon took %f seconds.", duration);
       
   450 #endif
       
   451 }
       
   452 
       
   453 // This hashing String->filename algorithm came from WebFileDatabase.m and is what was used in the 
       
   454 // WebKit Icon Database
       
   455 static void legacyIconDatabaseFilePathForKey(id key, char *buffer)
       
   456 {
       
   457     const char *s;
       
   458     UInt32 hash1;
       
   459     UInt32 hash2;
       
   460     CFIndex len;
       
   461     CFIndex cnt;
       
   462     
       
   463     s = [[[[key description] lowercaseString] stringByStandardizingPath] UTF8String];
       
   464     len = strlen(s);
       
   465 
       
   466     // compute first hash    
       
   467     hash1 = len;
       
   468     for (cnt = 0; cnt < len; cnt++) {
       
   469         hash1 += (hash1 << 8) + s[cnt];
       
   470     }
       
   471     hash1 += (hash1 << (len & 31));
       
   472 
       
   473     // compute second hash    
       
   474     hash2 = len;
       
   475     for (cnt = 0; cnt < len; cnt++) {
       
   476         hash2 = (37 * hash2) ^ s[cnt];
       
   477     }
       
   478 
       
   479 #ifdef __LP64__
       
   480     snprintf(buffer, UniqueFilePathSize, "%.2u/%.2u/%.10u-%.10u.cache", ((hash1 & 0xff) >> 4), ((hash2 & 0xff) >> 4), hash1, hash2);
       
   481 #else
       
   482     snprintf(buffer, UniqueFilePathSize, "%.2lu/%.2lu/%.10lu-%.10lu.cache", ((hash1 & 0xff) >> 4), ((hash2 & 0xff) >> 4), hash1, hash2);
       
   483 #endif
       
   484 }
       
   485 
       
   486 // This method of getting an object from the filesystem is taken from the old 
       
   487 // WebKit Icon Database
       
   488 static id objectFromPathForKey(NSString *databasePath, id key)
       
   489 {
       
   490     ASSERT(key);
       
   491     id result = nil;
       
   492 
       
   493     // Use the key->filename hashing the old WebKit IconDatabase used
       
   494     char uniqueKey[UniqueFilePathSize];    
       
   495     legacyIconDatabaseFilePathForKey(key, uniqueKey);
       
   496     
       
   497     // Get the data from this file and setup for the un-archiving
       
   498     NSString *filePath = [[NSString alloc] initWithFormat:@"%@/%s", databasePath, uniqueKey];
       
   499     NSData *data = [[NSData alloc] initWithContentsOfFile:filePath];
       
   500     NSUnarchiver *unarchiver = nil;
       
   501     
       
   502     @try {
       
   503         if (data) {
       
   504             unarchiver = [[NSUnarchiver alloc] initForReadingWithData:data];
       
   505             if (unarchiver) {
       
   506                 id fileKey = [unarchiver decodeObject];
       
   507                 if ([fileKey isEqual:key]) {
       
   508                     id object = [unarchiver decodeObject];
       
   509                     if (object) {
       
   510                         // Decoded objects go away when the unarchiver does, so we need to
       
   511                         // retain this so we can return it to our caller.
       
   512                         result = [[object retain] autorelease];
       
   513                         LOG(IconDatabase, "read disk cache file - %@", key);
       
   514                     }
       
   515                 }
       
   516             }
       
   517         }
       
   518     } @catch (NSException *localException) {
       
   519         LOG(IconDatabase, "cannot unarchive cache file - %@", key);
       
   520         result = nil;
       
   521     }
       
   522 
       
   523     [unarchiver release];
       
   524     [data release];
       
   525     [filePath release];
       
   526     
       
   527     return result;
       
   528 }
       
   529 
       
   530 static NSData* iconDataFromPathForIconURL(NSString *databasePath, NSString *iconURLString)
       
   531 {
       
   532     ASSERT(iconURLString);
       
   533     ASSERT(databasePath);
       
   534     
       
   535     NSData *iconData = objectFromPathForKey(databasePath, iconURLString);
       
   536     
       
   537     if ((id)iconData == (id)[NSNull null]) 
       
   538         return nil;
       
   539         
       
   540     return iconData;
       
   541 }
       
   542 
       
   543 - (NSString *)_databaseDirectory
       
   544 {
       
   545     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
       
   546 
       
   547     // Figure out the directory we should be using for the icon.db
       
   548     NSString *databaseDirectory = [defaults objectForKey:WebIconDatabaseDirectoryDefaultsKey];
       
   549     if (!databaseDirectory) {
       
   550         databaseDirectory = WebIconDatabasePath;
       
   551         [defaults setObject:databaseDirectory forKey:WebIconDatabaseDirectoryDefaultsKey];
       
   552     }
       
   553     
       
   554     return [[databaseDirectory stringByExpandingTildeInPath] stringByStandardizingPath];
       
   555 }
       
   556 
       
   557 @end
       
   558 
       
   559 @implementation WebIconDatabasePrivate
       
   560 @end
       
   561 
       
   562 @interface ThreadEnabler : NSObject {
       
   563 }
       
   564 + (void)enableThreading;
       
   565 
       
   566 - (void)threadEnablingSelector:(id)arg;
       
   567 @end
       
   568 
       
   569 @implementation ThreadEnabler
       
   570 
       
   571 - (void)threadEnablingSelector:(id)arg
       
   572 {
       
   573     return;
       
   574 }
       
   575 
       
   576 + (void)enableThreading
       
   577 {
       
   578     ThreadEnabler *enabler = [[ThreadEnabler alloc] init];
       
   579     [NSThread detachNewThreadSelector:@selector(threadEnablingSelector:) toTarget:enabler withObject:nil];
       
   580     [enabler release];
       
   581 }
       
   582 
       
   583 @end
       
   584 
       
   585 bool importToWebCoreFormat()
       
   586 {
       
   587     // Since this is running on a secondary POSIX thread and Cocoa cannot be used multithreaded unless an NSThread has been detached,
       
   588     // make sure that happens here for all WebKit clients
       
   589     if (![NSThread isMultiThreaded])
       
   590         [ThreadEnabler enableThreading];
       
   591     ASSERT([NSThread isMultiThreaded]);    
       
   592     
       
   593 #ifndef BUILDING_ON_TIGER 
       
   594     // Tell backup software (i.e., Time Machine) to never back up the icon database, because  
       
   595     // it's a large file that changes frequently, thus using a lot of backup disk space, and 
       
   596     // it's unlikely that many users would be upset about it not being backed up. We do this 
       
   597     // here because this code is only executed once for each icon database instance. We could 
       
   598     // make this configurable on a per-client basis someday if that seemed useful. 
       
   599     // See <rdar://problem/5320208>.
       
   600     // FIXME: This has nothing to do with importing from the old to the new database format and should be moved elsewhere,
       
   601     // especially because we might eventually delete all of this legacy importing code and we shouldn't delete this.
       
   602     CFStringRef databasePath = iconDatabase()->databasePath().createCFString();
       
   603     if (databasePath) {
       
   604         CFURLRef databasePathURL = CFURLCreateWithFileSystemPath(0, databasePath, kCFURLPOSIXPathStyle, FALSE); 
       
   605         CFRelease(databasePath);
       
   606         CSBackupSetItemExcluded(databasePathURL, true, true); 
       
   607         CFRelease(databasePathURL);
       
   608     }
       
   609 #endif 
       
   610 
       
   611     // Get the directory the old icon database *should* be in
       
   612     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
       
   613     NSString *databaseDirectory = [defaults objectForKey:WebIconDatabaseImportDirectoryDefaultsKey];
       
   614     
       
   615     if (!databaseDirectory)
       
   616         databaseDirectory = [defaults objectForKey:WebIconDatabaseDirectoryDefaultsKey];
       
   617         
       
   618     if (!databaseDirectory) {
       
   619         databaseDirectory = WebIconDatabasePath;
       
   620         [defaults setObject:databaseDirectory forKey:WebIconDatabaseDirectoryDefaultsKey];
       
   621     }
       
   622     databaseDirectory = [databaseDirectory stringByExpandingTildeInPath];
       
   623 
       
   624     // With this directory, get the PageURLToIconURL map that was saved to disk
       
   625     NSMutableDictionary *pageURLToIconURL = objectFromPathForKey(databaseDirectory, WebURLToIconURLKey);
       
   626 
       
   627     // If the retrieved object was not a valid NSMutableDictionary, then we have no valid
       
   628     // icons to import
       
   629     if (![pageURLToIconURL isKindOfClass:[NSMutableDictionary class]])
       
   630         pageURLToIconURL = nil;
       
   631     
       
   632     if (!pageURLToIconURL) {
       
   633         // We found no Safari-2-style icon database. Bail out immediately and do not delete everything
       
   634         // in whatever directory we ended up looking in! Return true so we won't bother to check again.
       
   635         // FIXME: We can probably delete all of the code to convert Safari-2-style icon databases now.
       
   636         return true;
       
   637     }
       
   638     
       
   639     NSEnumerator *enumerator = [pageURLToIconURL keyEnumerator];
       
   640     NSString *url, *iconURL;
       
   641     
       
   642     // First, we'll iterate through the PageURL->IconURL map
       
   643     while ((url = [enumerator nextObject]) != nil) {
       
   644         iconURL = [pageURLToIconURL objectForKey:url];
       
   645         if (!iconURL)
       
   646             continue;
       
   647         iconDatabase()->importIconURLForPageURL(iconURL, url);
       
   648         if (iconDatabase()->shouldStopThreadActivity())
       
   649             return false;
       
   650     }    
       
   651 
       
   652     // Second, we'll get a list of the unique IconURLs we have
       
   653     NSMutableSet *iconsOnDiskWithURLs = [NSMutableSet setWithArray:[pageURLToIconURL allValues]];
       
   654     enumerator = [iconsOnDiskWithURLs objectEnumerator];
       
   655     NSData *iconData;
       
   656     
       
   657     // And iterate through them, adding the icon data to the new icon database
       
   658     while ((url = [enumerator nextObject]) != nil) {
       
   659         iconData = iconDataFromPathForIconURL(databaseDirectory, url);
       
   660         if (iconData)
       
   661             iconDatabase()->importIconDataForIconURL(SharedBuffer::wrapNSData(iconData), url);
       
   662         else {
       
   663             // This really *shouldn't* happen, so it'd be good to track down why it might happen in a debug build
       
   664             // however, we do know how to handle it gracefully in release
       
   665             LOG_ERROR("%@ is marked as having an icon on disk, but we couldn't get the data for it", url);
       
   666             iconDatabase()->importIconDataForIconURL(0, url);
       
   667         }
       
   668         if (iconDatabase()->shouldStopThreadActivity())
       
   669             return false;
       
   670     }
       
   671     
       
   672     // After we're done importing old style icons over to webcore icons, we delete the entire directory hierarchy 
       
   673     // for the old icon DB (skipping the new iconDB if it is in the same directory)
       
   674     NSFileManager *fileManager = [NSFileManager defaultManager];
       
   675     enumerator = [[fileManager contentsOfDirectoryAtPath:databaseDirectory error:NULL] objectEnumerator];
       
   676 
       
   677     NSString *databaseFilename = iconDatabase()->defaultDatabaseFilename();
       
   678 
       
   679     BOOL foundIconDB = NO;
       
   680     NSString *file;
       
   681     while ((file = [enumerator nextObject]) != nil) {
       
   682         if ([file caseInsensitiveCompare:databaseFilename] == NSOrderedSame) {
       
   683             foundIconDB = YES;
       
   684             continue;
       
   685         }
       
   686         NSString *filePath = [databaseDirectory stringByAppendingPathComponent:file];
       
   687         if (![fileManager removeItemAtPath:filePath error:NULL])
       
   688             LOG_ERROR("Failed to delete %@ from old icon directory", filePath);
       
   689     }
       
   690     
       
   691     // If the new iconDB wasn't in that directory, we can delete the directory itself
       
   692     if (!foundIconDB)
       
   693         rmdir([databaseDirectory fileSystemRepresentation]);
       
   694     
       
   695     return true;
       
   696 }
       
   697 
       
   698 NSImage *webGetNSImage(Image* image, NSSize size)
       
   699 {
       
   700     ASSERT_MAIN_THREAD();
       
   701     ASSERT(size.width);
       
   702     ASSERT(size.height);
       
   703 
       
   704     // FIXME: We're doing the resize here for now because WebCore::Image doesn't yet support resizing/multiple representations
       
   705     // This makes it so there's effectively only one size of a particular icon in the system at a time. We should move this
       
   706     // to WebCore::Image at some point.
       
   707     if (!image)
       
   708         return nil;
       
   709     NSImage* nsImage = image->getNSImage();
       
   710     if (!nsImage)
       
   711         return nil;
       
   712     if (!NSEqualSizes([nsImage size], size)) {
       
   713         [nsImage setScalesWhenResized:YES];
       
   714         [nsImage setSize:size];
       
   715     }
       
   716     return nsImage;
       
   717 }