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