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