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