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 "WebPreferencesPrivate.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().synchronousIconForPageURL(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().synchronousIconURLForPageURL(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, IconDatabase::defaultDatabaseFilename()))
    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:WebPreferencesChangedInternalNotification
    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:WebPreferencesChangedInternalNotification
    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     // Get the directory the old icon database *should* be in
    594     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    595     NSString *databaseDirectory = [defaults objectForKey:WebIconDatabaseImportDirectoryDefaultsKey];
    596 
    597     if (!databaseDirectory)
    598         databaseDirectory = [defaults objectForKey:WebIconDatabaseDirectoryDefaultsKey];
    599 
    600     if (!databaseDirectory) {
    601         databaseDirectory = WebIconDatabasePath;
    602         [defaults setObject:databaseDirectory forKey:WebIconDatabaseDirectoryDefaultsKey];
    603     }
    604     databaseDirectory = [databaseDirectory stringByExpandingTildeInPath];
    605 
    606     // With this directory, get the PageURLToIconURL map that was saved to disk
    607     NSMutableDictionary *pageURLToIconURL = objectFromPathForKey(databaseDirectory, WebURLToIconURLKey);
    608 
    609     // If the retrieved object was not a valid NSMutableDictionary, then we have no valid
    610     // icons to import
    611     if (![pageURLToIconURL isKindOfClass:[NSMutableDictionary class]])
    612         pageURLToIconURL = nil;
    613 
    614     if (!pageURLToIconURL) {
    615         // We found no Safari-2-style icon database. Bail out immediately and do not delete everything
    616         // in whatever directory we ended up looking in! Return true so we won't bother to check again.
    617         // FIXME: We can probably delete all of the code to convert Safari-2-style icon databases now.
    618         return true;
    619     }
    620 
    621     NSEnumerator *enumerator = [pageURLToIconURL keyEnumerator];
    622     NSString *url, *iconURL;
    623 
    624     // First, we'll iterate through the PageURL->IconURL map
    625     while ((url = [enumerator nextObject]) != nil) {
    626         iconURL = [pageURLToIconURL objectForKey:url];
    627         if (!iconURL)
    628             continue;
    629         iconDatabase().importIconURLForPageURL(iconURL, url);
    630         if (iconDatabase().shouldStopThreadActivity())
    631             return false;
    632     }
    633 
    634     // Second, we'll get a list of the unique IconURLs we have
    635     NSMutableSet *iconsOnDiskWithURLs = [NSMutableSet setWithArray:[pageURLToIconURL allValues]];
    636     enumerator = [iconsOnDiskWithURLs objectEnumerator];
    637     NSData *iconData;
    638 
    639     // And iterate through them, adding the icon data to the new icon database
    640     while ((url = [enumerator nextObject]) != nil) {
    641         iconData = iconDataFromPathForIconURL(databaseDirectory, url);
    642         if (iconData)
    643             iconDatabase().importIconDataForIconURL(SharedBuffer::wrapNSData(iconData), url);
    644         else {
    645             // This really *shouldn't* happen, so it'd be good to track down why it might happen in a debug build
    646             // however, we do know how to handle it gracefully in release
    647             LOG_ERROR("%@ is marked as having an icon on disk, but we couldn't get the data for it", url);
    648             iconDatabase().importIconDataForIconURL(0, url);
    649         }
    650         if (iconDatabase().shouldStopThreadActivity())
    651             return false;
    652     }
    653 
    654     // After we're done importing old style icons over to webcore icons, we delete the entire directory hierarchy
    655     // for the old icon DB (skipping the new iconDB if it is in the same directory)
    656     NSFileManager *fileManager = [NSFileManager defaultManager];
    657     enumerator = [[fileManager contentsOfDirectoryAtPath:databaseDirectory error:NULL] objectEnumerator];
    658 
    659     NSString *databaseFilename = IconDatabase::defaultDatabaseFilename();
    660 
    661     BOOL foundIconDB = NO;
    662     NSString *file;
    663     while ((file = [enumerator nextObject]) != nil) {
    664         if ([file caseInsensitiveCompare:databaseFilename] == NSOrderedSame) {
    665             foundIconDB = YES;
    666             continue;
    667         }
    668         NSString *filePath = [databaseDirectory stringByAppendingPathComponent:file];
    669         if (![fileManager removeItemAtPath:filePath error:NULL])
    670             LOG_ERROR("Failed to delete %@ from old icon directory", filePath);
    671     }
    672 
    673     // If the new iconDB wasn't in that directory, we can delete the directory itself
    674     if (!foundIconDB)
    675         rmdir([databaseDirectory fileSystemRepresentation]);
    676 
    677     return true;
    678 }
    679 
    680 NSImage *webGetNSImage(Image* image, NSSize size)
    681 {
    682     ASSERT_MAIN_THREAD();
    683     ASSERT(size.width);
    684     ASSERT(size.height);
    685 
    686     // FIXME: We're doing the resize here for now because WebCore::Image doesn't yet support resizing/multiple representations
    687     // This makes it so there's effectively only one size of a particular icon in the system at a time. We should move this
    688     // to WebCore::Image at some point.
    689     if (!image)
    690         return nil;
    691     NSImage* nsImage = image->getNSImage();
    692     if (!nsImage)
    693         return nil;
    694     if (!NSEqualSizes([nsImage size], size)) {
    695         [nsImage setScalesWhenResized:YES];
    696         [nsImage setSize:size];
    697     }
    698     return nsImage;
    699 }
    700