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