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