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
48using namespace WebCore;
49
50NSString * const WebIconDatabaseVersionKey =    @"WebIconDatabaseVersion";
51NSString * const WebURLToIconURLKey =           @"WebSiteURLToIconURLKey";
52
53NSString *WebIconDatabaseDidAddIconNotification =          @"WebIconDatabaseDidAddIconNotification";
54NSString *WebIconNotificationUserInfoURLKey =              @"WebIconNotificationUserInfoURLKey";
55NSString *WebIconDatabaseDidRemoveAllIconsNotification =   @"WebIconDatabaseDidRemoveAllIconsNotification";
56
57NSString *WebIconDatabaseDirectoryDefaultsKey = @"WebIconDatabaseDirectoryDefaultsKey";
58NSString *WebIconDatabaseImportDirectoryDefaultsKey = @"WebIconDatabaseImportDirectoryDefaultsKey";
59NSString *WebIconDatabaseEnabledDefaultsKey =   @"WebIconDatabaseEnabled";
60
61NSString *WebIconDatabasePath = @"~/Library/Icons";
62
63NSSize WebIconSmallSize = {16, 16};
64NSSize WebIconMediumSize = {32, 32};
65NSSize WebIconLargeSize = {128, 128};
66
67#define UniqueFilePathSize (34)
68
69static 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
455static 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
488static 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
530static 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
585bool 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
680NSImage *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