|
1 /* |
|
2 * Copyright (C) 2005, 2006 Apple Computer, 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 "WebNSNotificationCenterExtras.h" |
|
36 #import "WebNSURLExtras.h" |
|
37 #import "WebPreferences.h" |
|
38 #import "WebTypesInternal.h" |
|
39 #import <WebCore/FoundationExtras.h> |
|
40 #import <WebCore/IconDatabase.h> |
|
41 #import <WebCore/Image.h> |
|
42 #import <WebCore/IntSize.h> |
|
43 #import <WebCore/ThreadCheck.h> |
|
44 |
|
45 using namespace WebCore; |
|
46 |
|
47 NSString * const WebIconDatabaseVersionKey = @"WebIconDatabaseVersion"; |
|
48 NSString * const WebURLToIconURLKey = @"WebSiteURLToIconURLKey"; |
|
49 |
|
50 NSString *WebIconDatabaseDidAddIconNotification = @"WebIconDatabaseDidAddIconNotification"; |
|
51 NSString *WebIconNotificationUserInfoURLKey = @"WebIconNotificationUserInfoURLKey"; |
|
52 NSString *WebIconDatabaseDidRemoveAllIconsNotification = @"WebIconDatabaseDidRemoveAllIconsNotification"; |
|
53 |
|
54 NSString *WebIconDatabaseDirectoryDefaultsKey = @"WebIconDatabaseDirectoryDefaultsKey"; |
|
55 NSString *WebIconDatabaseImportDirectoryDefaultsKey = @"WebIconDatabaseImportDirectoryDefaultsKey"; |
|
56 NSString *WebIconDatabaseEnabledDefaultsKey = @"WebIconDatabaseEnabled"; |
|
57 |
|
58 NSString *WebIconDatabasePath = @"~/Library/Icons"; |
|
59 |
|
60 NSSize WebIconSmallSize = {16, 16}; |
|
61 NSSize WebIconMediumSize = {32, 32}; |
|
62 NSSize WebIconLargeSize = {128, 128}; |
|
63 |
|
64 #define UniqueFilePathSize (34) |
|
65 |
|
66 static WebIconDatabaseClient* defaultClient() |
|
67 { |
|
68 static WebIconDatabaseClient* defaultClient = new WebIconDatabaseClient(); |
|
69 return defaultClient; |
|
70 } |
|
71 |
|
72 @interface WebIconDatabase (WebReallyInternal) |
|
73 - (BOOL)_isEnabled; |
|
74 - (void)_sendNotificationForURL:(NSString *)URL; |
|
75 - (void)_sendDidRemoveAllIconsNotification; |
|
76 - (NSImage *)_iconForFileURL:(NSString *)fileURL withSize:(NSSize)size; |
|
77 - (void)_resetCachedWebPreferences:(NSNotification *)notification; |
|
78 - (NSImage *)_largestIconFromDictionary:(NSMutableDictionary *)icons; |
|
79 - (NSMutableDictionary *)_iconsBySplittingRepresentationsOfIcon:(NSImage *)icon; |
|
80 - (NSImage *)_iconFromDictionary:(NSMutableDictionary *)icons forSize:(NSSize)size cache:(BOOL)cache; |
|
81 - (void)_scaleIcon:(NSImage *)icon toSize:(NSSize)size; |
|
82 - (NSString *)_databaseDirectory; |
|
83 @end |
|
84 |
|
85 @implementation WebIconDatabase |
|
86 |
|
87 + (WebIconDatabase *)sharedIconDatabase |
|
88 { |
|
89 static WebIconDatabase *database = nil; |
|
90 if (!database) |
|
91 database = [[WebIconDatabase alloc] init]; |
|
92 return database; |
|
93 } |
|
94 |
|
95 - init |
|
96 { |
|
97 [super init]; |
|
98 WebCoreThreadViolationCheck(); |
|
99 |
|
100 _private = [[WebIconDatabasePrivate alloc] init]; |
|
101 |
|
102 // Check the user defaults and see if the icon database should even be enabled. |
|
103 // Inform the bridge and, if we're disabled, bail from init right here |
|
104 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; |
|
105 // <rdar://problem/4741419> - IconDatabase should be disabled by default |
|
106 NSDictionary *initialDefaults = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithBool:YES], WebIconDatabaseEnabledDefaultsKey, nil]; |
|
107 [defaults registerDefaults:initialDefaults]; |
|
108 [initialDefaults release]; |
|
109 BOOL enabled = [defaults boolForKey:WebIconDatabaseEnabledDefaultsKey]; |
|
110 iconDatabase()->setEnabled(enabled); |
|
111 if (!enabled) |
|
112 return self; |
|
113 iconDatabase()->setClient(defaultClient()); |
|
114 |
|
115 // Figure out the directory we should be using for the icon.db |
|
116 NSString *databaseDirectory = [self _databaseDirectory]; |
|
117 |
|
118 // Rename legacy icon database files to the new icon database name |
|
119 BOOL isDirectory = NO; |
|
120 NSString *legacyDB = [databaseDirectory stringByAppendingPathComponent:@"icon.db"]; |
|
121 NSFileManager *defaultManager = [NSFileManager defaultManager]; |
|
122 if ([defaultManager fileExistsAtPath:legacyDB isDirectory:&isDirectory] && !isDirectory) { |
|
123 NSString *newDB = [databaseDirectory stringByAppendingPathComponent:iconDatabase()->defaultDatabaseFilename()]; |
|
124 if (![defaultManager fileExistsAtPath:newDB]) |
|
125 rename([legacyDB fileSystemRepresentation], [newDB fileSystemRepresentation]); |
|
126 } |
|
127 |
|
128 // Set the private browsing pref then open the WebCore icon database |
|
129 iconDatabase()->setPrivateBrowsingEnabled([[WebPreferences standardPreferences] privateBrowsingEnabled]); |
|
130 if (!iconDatabase()->open(databaseDirectory)) |
|
131 LOG_ERROR("Unable to open icon database"); |
|
132 |
|
133 // Register for important notifications |
|
134 [[NSNotificationCenter defaultCenter] addObserver:self |
|
135 selector:@selector(_applicationWillTerminate:) |
|
136 name:NSApplicationWillTerminateNotification |
|
137 object:NSApp]; |
|
138 [[NSNotificationCenter defaultCenter] |
|
139 addObserver:self selector:@selector(_resetCachedWebPreferences:) |
|
140 name:WebPreferencesChangedNotification object:nil]; |
|
141 |
|
142 return self; |
|
143 } |
|
144 |
|
145 - (NSImage *)iconForURL:(NSString *)URL withSize:(NSSize)size cache:(BOOL)cache |
|
146 { |
|
147 ASSERT_MAIN_THREAD(); |
|
148 ASSERT(size.width); |
|
149 ASSERT(size.height); |
|
150 |
|
151 if (!URL || ![self _isEnabled]) |
|
152 return [self defaultIconForURL:URL withSize:size]; |
|
153 |
|
154 // FIXME - <rdar://problem/4697934> - Move the handling of FileURLs to WebCore and implement in ObjC++ |
|
155 if ([URL _webkit_isFileURL]) |
|
156 return [self _iconForFileURL:URL withSize:size]; |
|
157 |
|
158 if (Image* image = iconDatabase()->iconForPageURL(URL, IntSize(size))) |
|
159 if (NSImage *icon = webGetNSImage(image, size)) |
|
160 return icon; |
|
161 return [self defaultIconForURL:URL withSize:size]; |
|
162 } |
|
163 |
|
164 - (NSImage *)iconForURL:(NSString *)URL withSize:(NSSize)size |
|
165 { |
|
166 return [self iconForURL:URL withSize:size cache:YES]; |
|
167 } |
|
168 |
|
169 - (NSString *)iconURLForURL:(NSString *)URL |
|
170 { |
|
171 if (![self _isEnabled]) |
|
172 return nil; |
|
173 ASSERT_MAIN_THREAD(); |
|
174 |
|
175 return iconDatabase()->iconURLForPageURL(URL); |
|
176 } |
|
177 |
|
178 - (NSImage *)defaultIconWithSize:(NSSize)size |
|
179 { |
|
180 ASSERT_MAIN_THREAD(); |
|
181 ASSERT(size.width); |
|
182 ASSERT(size.height); |
|
183 |
|
184 Image* image = iconDatabase()->defaultIcon(IntSize(size)); |
|
185 return image ? image->getNSImage() : nil; |
|
186 } |
|
187 |
|
188 - (NSImage *)defaultIconForURL:(NSString *)URL withSize:(NSSize)size |
|
189 { |
|
190 if (_private->delegateImplementsDefaultIconForURL) |
|
191 return [_private->delegate webIconDatabase:self defaultIconForURL:URL withSize:size]; |
|
192 return [self defaultIconWithSize:size]; |
|
193 } |
|
194 |
|
195 - (void)retainIconForURL:(NSString *)URL |
|
196 { |
|
197 ASSERT_MAIN_THREAD(); |
|
198 ASSERT(URL); |
|
199 if (![self _isEnabled]) |
|
200 return; |
|
201 |
|
202 iconDatabase()->retainIconForPageURL(URL); |
|
203 } |
|
204 |
|
205 - (void)releaseIconForURL:(NSString *)pageURL |
|
206 { |
|
207 ASSERT_MAIN_THREAD(); |
|
208 ASSERT(pageURL); |
|
209 if (![self _isEnabled]) |
|
210 return; |
|
211 |
|
212 iconDatabase()->releaseIconForPageURL(pageURL); |
|
213 } |
|
214 |
|
215 + (void)delayDatabaseCleanup |
|
216 { |
|
217 ASSERT_MAIN_THREAD(); |
|
218 |
|
219 IconDatabase::delayDatabaseCleanup(); |
|
220 } |
|
221 |
|
222 + (void)allowDatabaseCleanup |
|
223 { |
|
224 ASSERT_MAIN_THREAD(); |
|
225 |
|
226 IconDatabase::allowDatabaseCleanup(); |
|
227 } |
|
228 |
|
229 - (void)setDelegate:(id)delegate |
|
230 { |
|
231 _private->delegate = delegate; |
|
232 _private->delegateImplementsDefaultIconForURL = [delegate respondsToSelector:@selector(webIconDatabase:defaultIconForURL:withSize:)]; |
|
233 } |
|
234 |
|
235 - (id)delegate |
|
236 { |
|
237 return _private->delegate; |
|
238 } |
|
239 |
|
240 @end |
|
241 |
|
242 |
|
243 @implementation WebIconDatabase (WebPendingPublic) |
|
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 - (BOOL)_isEnabled |
|
269 { |
|
270 return iconDatabase()->isEnabled(); |
|
271 } |
|
272 |
|
273 - (void)_sendNotificationForURL:(NSString *)URL |
|
274 { |
|
275 ASSERT(URL); |
|
276 |
|
277 NSDictionary *userInfo = [NSDictionary dictionaryWithObject:URL |
|
278 forKey:WebIconNotificationUserInfoURLKey]; |
|
279 |
|
280 [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:WebIconDatabaseDidAddIconNotification |
|
281 object:self |
|
282 userInfo:userInfo]; |
|
283 } |
|
284 |
|
285 - (void)_sendDidRemoveAllIconsNotification |
|
286 { |
|
287 [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:WebIconDatabaseDidRemoveAllIconsNotification |
|
288 object:self |
|
289 userInfo:nil]; |
|
290 } |
|
291 |
|
292 - (void)_applicationWillTerminate:(NSNotification *)notification |
|
293 { |
|
294 iconDatabase()->close(); |
|
295 } |
|
296 |
|
297 - (NSImage *)_iconForFileURL:(NSString *)file withSize:(NSSize)size |
|
298 { |
|
299 ASSERT_MAIN_THREAD(); |
|
300 ASSERT(size.width); |
|
301 ASSERT(size.height); |
|
302 |
|
303 NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; |
|
304 NSString *path = [[NSURL _web_URLWithDataAsString:file] path]; |
|
305 NSString *suffix = [path pathExtension]; |
|
306 NSImage *icon = nil; |
|
307 |
|
308 if ([suffix _webkit_isCaseInsensitiveEqualToString:@"htm"] || [suffix _webkit_isCaseInsensitiveEqualToString:@"html"]) { |
|
309 if (!_private->htmlIcons) { |
|
310 icon = [workspace iconForFileType:@"html"]; |
|
311 _private->htmlIcons = [[self _iconsBySplittingRepresentationsOfIcon:icon] retain]; |
|
312 } |
|
313 icon = [self _iconFromDictionary:_private->htmlIcons forSize:size cache:YES]; |
|
314 } else { |
|
315 if (!path || ![path isAbsolutePath]) { |
|
316 // Return the generic icon when there is no path. |
|
317 icon = [workspace iconForFileType:NSFileTypeForHFSTypeCode(kGenericDocumentIcon)]; |
|
318 } else { |
|
319 icon = [workspace iconForFile:path]; |
|
320 } |
|
321 [self _scaleIcon:icon toSize:size]; |
|
322 } |
|
323 |
|
324 return icon; |
|
325 } |
|
326 |
|
327 - (void)_resetCachedWebPreferences:(NSNotification *)notification |
|
328 { |
|
329 BOOL privateBrowsingEnabledNow = [[WebPreferences standardPreferences] privateBrowsingEnabled]; |
|
330 iconDatabase()->setPrivateBrowsingEnabled(privateBrowsingEnabledNow); |
|
331 } |
|
332 |
|
333 - (NSImage *)_largestIconFromDictionary:(NSMutableDictionary *)icons |
|
334 { |
|
335 ASSERT(icons); |
|
336 |
|
337 NSEnumerator *enumerator = [icons keyEnumerator]; |
|
338 NSValue *currentSize, *largestSize=nil; |
|
339 float largestSizeArea=0; |
|
340 |
|
341 while ((currentSize = [enumerator nextObject]) != nil) { |
|
342 NSSize currentSizeSize = [currentSize sizeValue]; |
|
343 float currentSizeArea = currentSizeSize.width * currentSizeSize.height; |
|
344 if(!largestSizeArea || (currentSizeArea > largestSizeArea)){ |
|
345 largestSize = currentSize; |
|
346 largestSizeArea = currentSizeArea; |
|
347 } |
|
348 } |
|
349 |
|
350 return [icons objectForKey:largestSize]; |
|
351 } |
|
352 |
|
353 - (NSMutableDictionary *)_iconsBySplittingRepresentationsOfIcon:(NSImage *)icon |
|
354 { |
|
355 ASSERT(icon); |
|
356 |
|
357 NSMutableDictionary *icons = [NSMutableDictionary dictionary]; |
|
358 NSEnumerator *enumerator = [[icon representations] objectEnumerator]; |
|
359 NSImageRep *rep; |
|
360 |
|
361 while ((rep = [enumerator nextObject]) != nil) { |
|
362 NSSize size = [rep size]; |
|
363 NSImage *subIcon = [[NSImage alloc] initWithSize:size]; |
|
364 [subIcon addRepresentation:rep]; |
|
365 [icons setObject:subIcon forKey:[NSValue valueWithSize:size]]; |
|
366 [subIcon release]; |
|
367 } |
|
368 |
|
369 if([icons count] > 0) |
|
370 return icons; |
|
371 |
|
372 LOG_ERROR("icon has no representations"); |
|
373 |
|
374 return nil; |
|
375 } |
|
376 |
|
377 - (NSImage *)_iconFromDictionary:(NSMutableDictionary *)icons forSize:(NSSize)size cache:(BOOL)cache |
|
378 { |
|
379 ASSERT(size.width); |
|
380 ASSERT(size.height); |
|
381 |
|
382 NSImage *icon = [icons objectForKey:[NSValue valueWithSize:size]]; |
|
383 |
|
384 if(!icon){ |
|
385 icon = [[[self _largestIconFromDictionary:icons] copy] autorelease]; |
|
386 [self _scaleIcon:icon toSize:size]; |
|
387 |
|
388 if(cache){ |
|
389 [icons setObject:icon forKey:[NSValue valueWithSize:size]]; |
|
390 } |
|
391 } |
|
392 |
|
393 return icon; |
|
394 } |
|
395 |
|
396 - (void)_scaleIcon:(NSImage *)icon toSize:(NSSize)size |
|
397 { |
|
398 ASSERT(size.width); |
|
399 ASSERT(size.height); |
|
400 |
|
401 #if !LOG_DISABLED |
|
402 double start = CFAbsoluteTimeGetCurrent(); |
|
403 #endif |
|
404 |
|
405 [icon setScalesWhenResized:YES]; |
|
406 [icon setSize:size]; |
|
407 |
|
408 #if !LOG_DISABLED |
|
409 double duration = CFAbsoluteTimeGetCurrent() - start; |
|
410 LOG(Timing, "scaling icon took %f seconds.", duration); |
|
411 #endif |
|
412 } |
|
413 |
|
414 // This hashing String->filename algorithm came from WebFileDatabase.m and is what was used in the |
|
415 // WebKit Icon Database |
|
416 static void legacyIconDatabaseFilePathForKey(id key, char *buffer) |
|
417 { |
|
418 const char *s; |
|
419 UInt32 hash1; |
|
420 UInt32 hash2; |
|
421 CFIndex len; |
|
422 CFIndex cnt; |
|
423 |
|
424 s = [[[[key description] lowercaseString] stringByStandardizingPath] UTF8String]; |
|
425 len = strlen(s); |
|
426 |
|
427 // compute first hash |
|
428 hash1 = len; |
|
429 for (cnt = 0; cnt < len; cnt++) { |
|
430 hash1 += (hash1 << 8) + s[cnt]; |
|
431 } |
|
432 hash1 += (hash1 << (len & 31)); |
|
433 |
|
434 // compute second hash |
|
435 hash2 = len; |
|
436 for (cnt = 0; cnt < len; cnt++) { |
|
437 hash2 = (37 * hash2) ^ s[cnt]; |
|
438 } |
|
439 |
|
440 #ifdef __LP64__ |
|
441 snprintf(buffer, UniqueFilePathSize, "%.2u/%.2u/%.10u-%.10u.cache", ((hash1 & 0xff) >> 4), ((hash2 & 0xff) >> 4), hash1, hash2); |
|
442 #else |
|
443 snprintf(buffer, UniqueFilePathSize, "%.2lu/%.2lu/%.10lu-%.10lu.cache", ((hash1 & 0xff) >> 4), ((hash2 & 0xff) >> 4), hash1, hash2); |
|
444 #endif |
|
445 } |
|
446 |
|
447 // This method of getting an object from the filesystem is taken from the old |
|
448 // WebKit Icon Database |
|
449 static id objectFromPathForKey(NSString *databasePath, id key) |
|
450 { |
|
451 ASSERT(key); |
|
452 id result = nil; |
|
453 |
|
454 // Use the key->filename hashing the old WebKit IconDatabase used |
|
455 char uniqueKey[UniqueFilePathSize]; |
|
456 legacyIconDatabaseFilePathForKey(key, uniqueKey); |
|
457 |
|
458 // Get the data from this file and setup for the un-archiving |
|
459 NSString *filePath = [[NSString alloc] initWithFormat:@"%@/%s", databasePath, uniqueKey]; |
|
460 NSData *data = [[NSData alloc] initWithContentsOfFile:filePath]; |
|
461 NSUnarchiver *unarchiver = nil; |
|
462 |
|
463 NS_DURING |
|
464 if (data) { |
|
465 unarchiver = [[NSUnarchiver alloc] initForReadingWithData:data]; |
|
466 if (unarchiver) { |
|
467 id fileKey = [unarchiver decodeObject]; |
|
468 if ([fileKey isEqual:key]) { |
|
469 id object = [unarchiver decodeObject]; |
|
470 if (object) { |
|
471 // Decoded objects go away when the unarchiver does, so we need to |
|
472 // retain this so we can return it to our caller. |
|
473 result = [[object retain] autorelease]; |
|
474 LOG(IconDatabase, "read disk cache file - %@", key); |
|
475 } |
|
476 } |
|
477 } |
|
478 } |
|
479 NS_HANDLER |
|
480 LOG(IconDatabase, "cannot unarchive cache file - %@", key); |
|
481 result = nil; |
|
482 NS_ENDHANDLER |
|
483 |
|
484 [unarchiver release]; |
|
485 [data release]; |
|
486 [filePath release]; |
|
487 |
|
488 return result; |
|
489 } |
|
490 |
|
491 static NSData* iconDataFromPathForIconURL(NSString *databasePath, NSString *iconURLString) |
|
492 { |
|
493 ASSERT(iconURLString); |
|
494 ASSERT(databasePath); |
|
495 |
|
496 NSData *iconData = objectFromPathForKey(databasePath, iconURLString); |
|
497 |
|
498 if ((id)iconData == (id)[NSNull null]) |
|
499 return nil; |
|
500 |
|
501 return iconData; |
|
502 } |
|
503 |
|
504 - (NSString *)_databaseDirectory |
|
505 { |
|
506 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; |
|
507 |
|
508 // Figure out the directory we should be using for the icon.db |
|
509 NSString *databaseDirectory = [defaults objectForKey:WebIconDatabaseDirectoryDefaultsKey]; |
|
510 if (!databaseDirectory) { |
|
511 databaseDirectory = WebIconDatabasePath; |
|
512 [defaults setObject:databaseDirectory forKey:WebIconDatabaseDirectoryDefaultsKey]; |
|
513 } |
|
514 |
|
515 return [[databaseDirectory stringByExpandingTildeInPath] stringByStandardizingPath]; |
|
516 } |
|
517 |
|
518 @end |
|
519 |
|
520 @implementation WebIconDatabasePrivate |
|
521 @end |
|
522 |
|
523 @interface ThreadEnabler : NSObject { |
|
524 } |
|
525 + (void)enableThreading; |
|
526 |
|
527 - (void)threadEnablingSelector:(id)arg; |
|
528 @end |
|
529 |
|
530 @implementation ThreadEnabler |
|
531 |
|
532 - (void)threadEnablingSelector:(id)arg |
|
533 { |
|
534 return; |
|
535 } |
|
536 |
|
537 + (void)enableThreading |
|
538 { |
|
539 ThreadEnabler *enabler = [[ThreadEnabler alloc] init]; |
|
540 [NSThread detachNewThreadSelector:@selector(threadEnablingSelector:) toTarget:enabler withObject:nil]; |
|
541 [enabler release]; |
|
542 } |
|
543 |
|
544 @end |
|
545 |
|
546 bool importToWebCoreFormat() |
|
547 { |
|
548 // Since this is running on a secondary POSIX thread and Cocoa cannot be used multithreaded unless an NSThread has been detached, |
|
549 // make sure that happens here for all WebKit clients |
|
550 if (![NSThread isMultiThreaded]) |
|
551 [ThreadEnabler enableThreading]; |
|
552 ASSERT([NSThread isMultiThreaded]); |
|
553 |
|
554 #ifndef BUILDING_ON_TIGER |
|
555 // Tell backup software (i.e., Time Machine) to never back up the icon database, because |
|
556 // it's a large file that changes frequently, thus using a lot of backup disk space, and |
|
557 // it's unlikely that many users would be upset about it not being backed up. We do this |
|
558 // here because this code is only executed once for each icon database instance. We could |
|
559 // make this configurable on a per-client basis someday if that seemed useful. |
|
560 // See <rdar://problem/5320208>. |
|
561 CFStringRef databasePath = iconDatabase()->databasePath().createCFString(); |
|
562 if (databasePath) { |
|
563 CFURLRef databasePathURL = CFURLCreateWithFileSystemPath(0, databasePath, kCFURLPOSIXPathStyle, FALSE); |
|
564 CFRelease(databasePath); |
|
565 CSBackupSetItemExcluded(databasePathURL, true, true); |
|
566 CFRelease(databasePathURL); |
|
567 } |
|
568 #endif |
|
569 |
|
570 // Get the directory the old icon database *should* be in |
|
571 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; |
|
572 NSString *databaseDirectory = [defaults objectForKey:WebIconDatabaseImportDirectoryDefaultsKey]; |
|
573 |
|
574 if (!databaseDirectory) |
|
575 databaseDirectory = [defaults objectForKey:WebIconDatabaseDirectoryDefaultsKey]; |
|
576 |
|
577 if (!databaseDirectory) { |
|
578 databaseDirectory = WebIconDatabasePath; |
|
579 [defaults setObject:databaseDirectory forKey:WebIconDatabaseDirectoryDefaultsKey]; |
|
580 } |
|
581 databaseDirectory = [databaseDirectory stringByExpandingTildeInPath]; |
|
582 |
|
583 // With this directory, get the PageURLToIconURL map that was saved to disk |
|
584 NSMutableDictionary *pageURLToIconURL = objectFromPathForKey(databaseDirectory, WebURLToIconURLKey); |
|
585 |
|
586 // If the retrieved object was not a valid NSMutableDictionary, then we have no valid |
|
587 // icons to import |
|
588 if (![pageURLToIconURL isKindOfClass:[NSMutableDictionary class]]) |
|
589 pageURLToIconURL = nil; |
|
590 |
|
591 NSEnumerator *enumerator = [pageURLToIconURL keyEnumerator]; |
|
592 NSString *url, *iconURL; |
|
593 |
|
594 // First, we'll iterate through the PageURL->IconURL map |
|
595 while ((url = [enumerator nextObject]) != nil) { |
|
596 iconURL = [pageURLToIconURL objectForKey:url]; |
|
597 if (!iconURL) |
|
598 continue; |
|
599 iconDatabase()->importIconURLForPageURL(iconURL, url); |
|
600 if (iconDatabase()->shouldStopThreadActivity()) |
|
601 return false; |
|
602 } |
|
603 |
|
604 // Second, we'll get a list of the unique IconURLs we have |
|
605 NSMutableSet *iconsOnDiskWithURLs = [NSMutableSet setWithArray:[pageURLToIconURL allValues]]; |
|
606 enumerator = [iconsOnDiskWithURLs objectEnumerator]; |
|
607 NSData *iconData; |
|
608 |
|
609 // And iterate through them, adding the icon data to the new icon database |
|
610 while ((url = [enumerator nextObject]) != nil) { |
|
611 iconData = iconDataFromPathForIconURL(databaseDirectory, url); |
|
612 if (iconData) |
|
613 iconDatabase()->importIconDataForIconURL(SharedBuffer::wrapNSData(iconData), url); |
|
614 else { |
|
615 // This really *shouldn't* happen, so it'd be good to track down why it might happen in a debug build |
|
616 // however, we do know how to handle it gracefully in release |
|
617 LOG_ERROR("%@ is marked as having an icon on disk, but we couldn't get the data for it", url); |
|
618 iconDatabase()->importIconDataForIconURL(0, url); |
|
619 } |
|
620 if (iconDatabase()->shouldStopThreadActivity()) |
|
621 return false; |
|
622 } |
|
623 |
|
624 // After we're done importing old style icons over to webcore icons, we delete the entire directory hierarchy |
|
625 // for the old icon DB (skipping the new iconDB if it is in the same directory) |
|
626 NSFileManager *fileManager = [NSFileManager defaultManager]; |
|
627 enumerator = [[fileManager directoryContentsAtPath:databaseDirectory] objectEnumerator]; |
|
628 |
|
629 NSString *databaseFilename = iconDatabase()->defaultDatabaseFilename(); |
|
630 |
|
631 BOOL foundIconDB = NO; |
|
632 NSString *file; |
|
633 while ((file = [enumerator nextObject]) != nil) { |
|
634 if ([file caseInsensitiveCompare:databaseFilename] == NSOrderedSame) { |
|
635 foundIconDB = YES; |
|
636 continue; |
|
637 } |
|
638 NSString *filePath = [databaseDirectory stringByAppendingPathComponent:file]; |
|
639 if (![fileManager removeFileAtPath:filePath handler:nil]) |
|
640 LOG_ERROR("Failed to delete %@ from old icon directory", filePath); |
|
641 } |
|
642 |
|
643 // If the new iconDB wasn't in that directory, we can delete the directory itself |
|
644 if (!foundIconDB) |
|
645 rmdir([databaseDirectory fileSystemRepresentation]); |
|
646 |
|
647 return true; |
|
648 } |
|
649 |
|
650 NSImage *webGetNSImage(Image* image, NSSize size) |
|
651 { |
|
652 ASSERT_MAIN_THREAD(); |
|
653 ASSERT(size.width); |
|
654 ASSERT(size.height); |
|
655 |
|
656 // FIXME: We're doing the resize here for now because WebCore::Image doesn't yet support resizing/multiple representations |
|
657 // This makes it so there's effectively only one size of a particular icon in the system at a time. We should move this |
|
658 // to WebCore::Image at some point. |
|
659 if (!image) |
|
660 return nil; |
|
661 NSImage* nsImage = image->getNSImage(); |
|
662 if (!nsImage) |
|
663 return nil; |
|
664 if (!NSEqualSizes([nsImage size], size)) { |
|
665 [nsImage setScalesWhenResized:YES]; |
|
666 [nsImage setSize:size]; |
|
667 } |
|
668 return nsImage; |
|
669 } |