WebIconDatabase.mm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  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. #import "WebIconDatabaseInternal.h"
  29. #import "WebIconDatabaseClient.h"
  30. #import "WebIconDatabaseDelegate.h"
  31. #import "WebKitLogging.h"
  32. #import "WebKitNSStringExtras.h"
  33. #import "WebNSFileManagerExtras.h"
  34. #import "WebNSNotificationCenterExtras.h"
  35. #import "WebNSURLExtras.h"
  36. #import "WebPreferencesPrivate.h"
  37. #import "WebTypesInternal.h"
  38. #import <WebCore/IconDatabase.h>
  39. #import <WebCore/Image.h>
  40. #import <WebCore/IntSize.h>
  41. #import <WebCore/RunLoop.h>
  42. #import <WebCore/SharedBuffer.h>
  43. #import <WebCore/ThreadCheck.h>
  44. #import <runtime/InitializeThreading.h>
  45. #import <wtf/MainThread.h>
  46. using namespace WebCore;
  47. NSString * const WebIconDatabaseVersionKey = @"WebIconDatabaseVersion";
  48. NSString * const WebURLToIconURLKey = @"WebSiteURLToIconURLKey";
  49. NSString *WebIconDatabaseDidAddIconNotification = @"WebIconDatabaseDidAddIconNotification";
  50. NSString *WebIconNotificationUserInfoURLKey = @"WebIconNotificationUserInfoURLKey";
  51. NSString *WebIconDatabaseDidRemoveAllIconsNotification = @"WebIconDatabaseDidRemoveAllIconsNotification";
  52. NSString *WebIconDatabaseDirectoryDefaultsKey = @"WebIconDatabaseDirectoryDefaultsKey";
  53. NSString *WebIconDatabaseEnabledDefaultsKey = @"WebIconDatabaseEnabled";
  54. NSString *WebIconDatabasePath = @"~/Library/Icons";
  55. NSSize WebIconSmallSize = {16, 16};
  56. NSSize WebIconMediumSize = {32, 32};
  57. NSSize WebIconLargeSize = {128, 128};
  58. #define UniqueFilePathSize (34)
  59. static WebIconDatabaseClient* defaultClient()
  60. {
  61. #if ENABLE(ICONDATABASE)
  62. static WebIconDatabaseClient* defaultClient = new WebIconDatabaseClient();
  63. return defaultClient;
  64. #else
  65. return 0;
  66. #endif
  67. }
  68. @interface WebIconDatabase (WebReallyInternal)
  69. - (void)_sendNotificationForURL:(NSString *)URL;
  70. - (void)_sendDidRemoveAllIconsNotification;
  71. - (NSImage *)_iconForFileURL:(NSString *)fileURL withSize:(NSSize)size;
  72. - (void)_resetCachedWebPreferences:(NSNotification *)notification;
  73. - (NSImage *)_largestIconFromDictionary:(NSMutableDictionary *)icons;
  74. - (NSMutableDictionary *)_iconsBySplittingRepresentationsOfIcon:(NSImage *)icon;
  75. - (NSImage *)_iconFromDictionary:(NSMutableDictionary *)icons forSize:(NSSize)size cache:(BOOL)cache;
  76. - (void)_scaleIcon:(NSImage *)icon toSize:(NSSize)size;
  77. - (NSString *)_databaseDirectory;
  78. @end
  79. @implementation WebIconDatabase
  80. + (void)initialize
  81. {
  82. JSC::initializeThreading();
  83. WTF::initializeMainThreadToProcessMainThread();
  84. WebCore::RunLoop::initializeMainRunLoop();
  85. }
  86. + (WebIconDatabase *)sharedIconDatabase
  87. {
  88. static WebIconDatabase *database = nil;
  89. if (!database)
  90. database = [[WebIconDatabase alloc] init];
  91. return database;
  92. }
  93. - (id)init
  94. {
  95. self = [super init];
  96. if (!self)
  97. return nil;
  98. WebCoreThreadViolationCheckRoundOne();
  99. _private = [[WebIconDatabasePrivate alloc] init];
  100. // Check the user defaults and see if the icon database should even be enabled.
  101. // Inform the bridge and, if we're disabled, bail from init right here
  102. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  103. // <rdar://problem/4741419> - IconDatabase should be disabled by default
  104. NSDictionary *initialDefaults = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithBool:YES], WebIconDatabaseEnabledDefaultsKey, nil];
  105. [defaults registerDefaults:initialDefaults];
  106. [initialDefaults release];
  107. BOOL enabled = [defaults boolForKey:WebIconDatabaseEnabledDefaultsKey];
  108. iconDatabase().setEnabled(enabled);
  109. if (enabled)
  110. [self _startUpIconDatabase];
  111. return self;
  112. }
  113. - (NSImage *)iconForURL:(NSString *)URL withSize:(NSSize)size cache:(BOOL)cache
  114. {
  115. ASSERT_MAIN_THREAD();
  116. ASSERT(size.width);
  117. ASSERT(size.height);
  118. if (!URL || ![self isEnabled])
  119. return [self defaultIconForURL:URL withSize:size];
  120. // FIXME - <rdar://problem/4697934> - Move the handling of FileURLs to WebCore and implement in ObjC++
  121. if ([URL _webkit_isFileURL])
  122. return [self _iconForFileURL:URL withSize:size];
  123. if (Image* image = iconDatabase().synchronousIconForPageURL(URL, IntSize(size)))
  124. if (NSImage *icon = webGetNSImage(image, size))
  125. return icon;
  126. return [self defaultIconForURL:URL withSize:size];
  127. }
  128. - (NSImage *)iconForURL:(NSString *)URL withSize:(NSSize)size
  129. {
  130. return [self iconForURL:URL withSize:size cache:YES];
  131. }
  132. - (NSString *)iconURLForURL:(NSString *)URL
  133. {
  134. if (![self isEnabled])
  135. return nil;
  136. ASSERT_MAIN_THREAD();
  137. return iconDatabase().synchronousIconURLForPageURL(URL);
  138. }
  139. - (NSImage *)defaultIconWithSize:(NSSize)size
  140. {
  141. ASSERT_MAIN_THREAD();
  142. ASSERT(size.width);
  143. ASSERT(size.height);
  144. Image* image = iconDatabase().defaultIcon(IntSize(size));
  145. return image ? image->getNSImage() : nil;
  146. }
  147. - (NSImage *)defaultIconForURL:(NSString *)URL withSize:(NSSize)size
  148. {
  149. if (_private->delegateImplementsDefaultIconForURL)
  150. return [_private->delegate webIconDatabase:self defaultIconForURL:URL withSize:size];
  151. return [self defaultIconWithSize:size];
  152. }
  153. - (void)retainIconForURL:(NSString *)URL
  154. {
  155. ASSERT_MAIN_THREAD();
  156. ASSERT(URL);
  157. if (![self isEnabled])
  158. return;
  159. iconDatabase().retainIconForPageURL(URL);
  160. }
  161. - (void)releaseIconForURL:(NSString *)pageURL
  162. {
  163. ASSERT_MAIN_THREAD();
  164. ASSERT(pageURL);
  165. if (![self isEnabled])
  166. return;
  167. iconDatabase().releaseIconForPageURL(pageURL);
  168. }
  169. + (void)delayDatabaseCleanup
  170. {
  171. ASSERT_MAIN_THREAD();
  172. IconDatabase::delayDatabaseCleanup();
  173. }
  174. + (void)allowDatabaseCleanup
  175. {
  176. ASSERT_MAIN_THREAD();
  177. IconDatabase::allowDatabaseCleanup();
  178. }
  179. - (void)setDelegate:(id)delegate
  180. {
  181. _private->delegate = delegate;
  182. _private->delegateImplementsDefaultIconForURL = [delegate respondsToSelector:@selector(webIconDatabase:defaultIconForURL:withSize:)];
  183. }
  184. - (id)delegate
  185. {
  186. return _private->delegate;
  187. }
  188. @end
  189. @implementation WebIconDatabase (WebPendingPublic)
  190. - (BOOL)isEnabled
  191. {
  192. return iconDatabase().isEnabled();
  193. }
  194. - (void)setEnabled:(BOOL)flag
  195. {
  196. BOOL currentlyEnabled = [self isEnabled];
  197. if (currentlyEnabled && !flag) {
  198. iconDatabase().setEnabled(false);
  199. [self _shutDownIconDatabase];
  200. } else if (!currentlyEnabled && flag) {
  201. iconDatabase().setEnabled(true);
  202. [self _startUpIconDatabase];
  203. }
  204. }
  205. - (void)removeAllIcons
  206. {
  207. ASSERT_MAIN_THREAD();
  208. if (![self isEnabled])
  209. return;
  210. // Via the IconDatabaseClient interface, removeAllIcons() will send the WebIconDatabaseDidRemoveAllIconsNotification
  211. iconDatabase().removeAllIcons();
  212. }
  213. @end
  214. @implementation WebIconDatabase (WebPrivate)
  215. + (void)_checkIntegrityBeforeOpening
  216. {
  217. IconDatabase::checkIntegrityBeforeOpening();
  218. }
  219. @end
  220. @implementation WebIconDatabase (WebInternal)
  221. - (void)_sendNotificationForURL:(NSString *)URL
  222. {
  223. ASSERT(URL);
  224. NSDictionary *userInfo = [NSDictionary dictionaryWithObject:URL
  225. forKey:WebIconNotificationUserInfoURLKey];
  226. [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:WebIconDatabaseDidAddIconNotification
  227. object:self
  228. userInfo:userInfo];
  229. }
  230. - (void)_sendDidRemoveAllIconsNotification
  231. {
  232. [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:WebIconDatabaseDidRemoveAllIconsNotification
  233. object:self
  234. userInfo:nil];
  235. }
  236. - (void)_startUpIconDatabase
  237. {
  238. iconDatabase().setClient(defaultClient());
  239. // Figure out the directory we should be using for the icon.db
  240. NSString *databaseDirectory = [self _databaseDirectory];
  241. // Rename legacy icon database files to the new icon database name
  242. BOOL isDirectory = NO;
  243. NSString *legacyDB = [databaseDirectory stringByAppendingPathComponent:@"icon.db"];
  244. NSFileManager *defaultManager = [NSFileManager defaultManager];
  245. if ([defaultManager fileExistsAtPath:legacyDB isDirectory:&isDirectory] && !isDirectory) {
  246. NSString *newDB = [databaseDirectory stringByAppendingPathComponent:IconDatabase::defaultDatabaseFilename()];
  247. if (![defaultManager fileExistsAtPath:newDB])
  248. rename([legacyDB fileSystemRepresentation], [newDB fileSystemRepresentation]);
  249. }
  250. // Set the private browsing pref then open the WebCore icon database
  251. iconDatabase().setPrivateBrowsingEnabled([[WebPreferences standardPreferences] privateBrowsingEnabled]);
  252. if (!iconDatabase().open(databaseDirectory, IconDatabase::defaultDatabaseFilename()))
  253. LOG_ERROR("Unable to open icon database");
  254. // Register for important notifications
  255. [[NSNotificationCenter defaultCenter] addObserver:self
  256. selector:@selector(_applicationWillTerminate:)
  257. name:NSApplicationWillTerminateNotification
  258. object:NSApp];
  259. [[NSNotificationCenter defaultCenter] addObserver:self
  260. selector:@selector(_resetCachedWebPreferences:)
  261. name:WebPreferencesChangedInternalNotification
  262. object:nil];
  263. }
  264. - (void)_shutDownIconDatabase
  265. {
  266. // Unregister for important notifications
  267. [[NSNotificationCenter defaultCenter] removeObserver:self
  268. name:NSApplicationWillTerminateNotification
  269. object:NSApp];
  270. [[NSNotificationCenter defaultCenter] removeObserver:self
  271. name:WebPreferencesChangedInternalNotification
  272. object:nil];
  273. }
  274. - (void)_applicationWillTerminate:(NSNotification *)notification
  275. {
  276. iconDatabase().close();
  277. }
  278. - (NSImage *)_iconForFileURL:(NSString *)file withSize:(NSSize)size
  279. {
  280. ASSERT_MAIN_THREAD();
  281. ASSERT(size.width);
  282. ASSERT(size.height);
  283. NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
  284. NSString *path = [[NSURL _web_URLWithDataAsString:file] path];
  285. NSString *suffix = [path pathExtension];
  286. NSImage *icon = nil;
  287. if ([suffix _webkit_isCaseInsensitiveEqualToString:@"htm"] || [suffix _webkit_isCaseInsensitiveEqualToString:@"html"]) {
  288. if (!_private->htmlIcons) {
  289. icon = [workspace iconForFileType:@"html"];
  290. _private->htmlIcons = [[self _iconsBySplittingRepresentationsOfIcon:icon] retain];
  291. }
  292. icon = [self _iconFromDictionary:_private->htmlIcons forSize:size cache:YES];
  293. } else {
  294. if (!path || ![path isAbsolutePath]) {
  295. // Return the generic icon when there is no path.
  296. icon = [workspace iconForFileType:NSFileTypeForHFSTypeCode(kGenericDocumentIcon)];
  297. } else {
  298. icon = [workspace iconForFile:path];
  299. }
  300. [self _scaleIcon:icon toSize:size];
  301. }
  302. return icon;
  303. }
  304. - (void)_resetCachedWebPreferences:(NSNotification *)notification
  305. {
  306. BOOL privateBrowsingEnabledNow = [[WebPreferences standardPreferences] privateBrowsingEnabled];
  307. iconDatabase().setPrivateBrowsingEnabled(privateBrowsingEnabledNow);
  308. }
  309. - (NSImage *)_largestIconFromDictionary:(NSMutableDictionary *)icons
  310. {
  311. ASSERT(icons);
  312. NSEnumerator *enumerator = [icons keyEnumerator];
  313. NSValue *currentSize, *largestSize=nil;
  314. float largestSizeArea=0;
  315. while ((currentSize = [enumerator nextObject]) != nil) {
  316. NSSize currentSizeSize = [currentSize sizeValue];
  317. float currentSizeArea = currentSizeSize.width * currentSizeSize.height;
  318. if(!largestSizeArea || (currentSizeArea > largestSizeArea)){
  319. largestSize = currentSize;
  320. largestSizeArea = currentSizeArea;
  321. }
  322. }
  323. return [icons objectForKey:largestSize];
  324. }
  325. - (NSMutableDictionary *)_iconsBySplittingRepresentationsOfIcon:(NSImage *)icon
  326. {
  327. ASSERT(icon);
  328. NSMutableDictionary *icons = [NSMutableDictionary dictionary];
  329. NSEnumerator *enumerator = [[icon representations] objectEnumerator];
  330. NSImageRep *rep;
  331. while ((rep = [enumerator nextObject]) != nil) {
  332. NSSize size = [rep size];
  333. NSImage *subIcon = [[NSImage alloc] initWithSize:size];
  334. [subIcon addRepresentation:rep];
  335. [icons setObject:subIcon forKey:[NSValue valueWithSize:size]];
  336. [subIcon release];
  337. }
  338. if([icons count] > 0)
  339. return icons;
  340. LOG_ERROR("icon has no representations");
  341. return nil;
  342. }
  343. - (NSImage *)_iconFromDictionary:(NSMutableDictionary *)icons forSize:(NSSize)size cache:(BOOL)cache
  344. {
  345. ASSERT(size.width);
  346. ASSERT(size.height);
  347. NSImage *icon = [icons objectForKey:[NSValue valueWithSize:size]];
  348. if(!icon){
  349. icon = [[[self _largestIconFromDictionary:icons] copy] autorelease];
  350. [self _scaleIcon:icon toSize:size];
  351. if(cache){
  352. [icons setObject:icon forKey:[NSValue valueWithSize:size]];
  353. }
  354. }
  355. return icon;
  356. }
  357. - (void)_scaleIcon:(NSImage *)icon toSize:(NSSize)size
  358. {
  359. ASSERT(size.width);
  360. ASSERT(size.height);
  361. #if !LOG_DISABLED
  362. double start = CFAbsoluteTimeGetCurrent();
  363. #endif
  364. [icon setScalesWhenResized:YES];
  365. [icon setSize:size];
  366. #if !LOG_DISABLED
  367. double duration = CFAbsoluteTimeGetCurrent() - start;
  368. LOG(Timing, "scaling icon took %f seconds.", duration);
  369. #endif
  370. }
  371. - (NSString *)_databaseDirectory
  372. {
  373. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  374. // Figure out the directory we should be using for the icon.db
  375. NSString *databaseDirectory = [defaults objectForKey:WebIconDatabaseDirectoryDefaultsKey];
  376. if (!databaseDirectory) {
  377. databaseDirectory = WebIconDatabasePath;
  378. [defaults setObject:databaseDirectory forKey:WebIconDatabaseDirectoryDefaultsKey];
  379. }
  380. return [[databaseDirectory stringByExpandingTildeInPath] stringByStandardizingPath];
  381. }
  382. @end
  383. @implementation WebIconDatabasePrivate
  384. @end
  385. NSImage *webGetNSImage(Image* image, NSSize size)
  386. {
  387. ASSERT_MAIN_THREAD();
  388. ASSERT(size.width);
  389. ASSERT(size.height);
  390. // FIXME: We're doing the resize here for now because WebCore::Image doesn't yet support resizing/multiple representations
  391. // This makes it so there's effectively only one size of a particular icon in the system at a time. We should move this
  392. // to WebCore::Image at some point.
  393. if (!image)
  394. return nil;
  395. NSImage* nsImage = image->getNSImage();
  396. if (!nsImage)
  397. return nil;
  398. if (!NSEqualSizes([nsImage size], size)) {
  399. [nsImage setScalesWhenResized:YES];
  400. [nsImage setSize:size];
  401. }
  402. return nsImage;
  403. }