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