|
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/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()->iconForPageURL(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()->iconURLForPageURL(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)) |
|
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:WebPreferencesChangedNotification |
|
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:WebPreferencesChangedNotification |
|
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 #ifndef BUILDING_ON_TIGER |
|
594 // Tell backup software (i.e., Time Machine) to never back up the icon database, because |
|
595 // it's a large file that changes frequently, thus using a lot of backup disk space, and |
|
596 // it's unlikely that many users would be upset about it not being backed up. We do this |
|
597 // here because this code is only executed once for each icon database instance. We could |
|
598 // make this configurable on a per-client basis someday if that seemed useful. |
|
599 // See <rdar://problem/5320208>. |
|
600 // FIXME: This has nothing to do with importing from the old to the new database format and should be moved elsewhere, |
|
601 // especially because we might eventually delete all of this legacy importing code and we shouldn't delete this. |
|
602 CFStringRef databasePath = iconDatabase()->databasePath().createCFString(); |
|
603 if (databasePath) { |
|
604 CFURLRef databasePathURL = CFURLCreateWithFileSystemPath(0, databasePath, kCFURLPOSIXPathStyle, FALSE); |
|
605 CFRelease(databasePath); |
|
606 CSBackupSetItemExcluded(databasePathURL, true, true); |
|
607 CFRelease(databasePathURL); |
|
608 } |
|
609 #endif |
|
610 |
|
611 // Get the directory the old icon database *should* be in |
|
612 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; |
|
613 NSString *databaseDirectory = [defaults objectForKey:WebIconDatabaseImportDirectoryDefaultsKey]; |
|
614 |
|
615 if (!databaseDirectory) |
|
616 databaseDirectory = [defaults objectForKey:WebIconDatabaseDirectoryDefaultsKey]; |
|
617 |
|
618 if (!databaseDirectory) { |
|
619 databaseDirectory = WebIconDatabasePath; |
|
620 [defaults setObject:databaseDirectory forKey:WebIconDatabaseDirectoryDefaultsKey]; |
|
621 } |
|
622 databaseDirectory = [databaseDirectory stringByExpandingTildeInPath]; |
|
623 |
|
624 // With this directory, get the PageURLToIconURL map that was saved to disk |
|
625 NSMutableDictionary *pageURLToIconURL = objectFromPathForKey(databaseDirectory, WebURLToIconURLKey); |
|
626 |
|
627 // If the retrieved object was not a valid NSMutableDictionary, then we have no valid |
|
628 // icons to import |
|
629 if (![pageURLToIconURL isKindOfClass:[NSMutableDictionary class]]) |
|
630 pageURLToIconURL = nil; |
|
631 |
|
632 if (!pageURLToIconURL) { |
|
633 // We found no Safari-2-style icon database. Bail out immediately and do not delete everything |
|
634 // in whatever directory we ended up looking in! Return true so we won't bother to check again. |
|
635 // FIXME: We can probably delete all of the code to convert Safari-2-style icon databases now. |
|
636 return true; |
|
637 } |
|
638 |
|
639 NSEnumerator *enumerator = [pageURLToIconURL keyEnumerator]; |
|
640 NSString *url, *iconURL; |
|
641 |
|
642 // First, we'll iterate through the PageURL->IconURL map |
|
643 while ((url = [enumerator nextObject]) != nil) { |
|
644 iconURL = [pageURLToIconURL objectForKey:url]; |
|
645 if (!iconURL) |
|
646 continue; |
|
647 iconDatabase()->importIconURLForPageURL(iconURL, url); |
|
648 if (iconDatabase()->shouldStopThreadActivity()) |
|
649 return false; |
|
650 } |
|
651 |
|
652 // Second, we'll get a list of the unique IconURLs we have |
|
653 NSMutableSet *iconsOnDiskWithURLs = [NSMutableSet setWithArray:[pageURLToIconURL allValues]]; |
|
654 enumerator = [iconsOnDiskWithURLs objectEnumerator]; |
|
655 NSData *iconData; |
|
656 |
|
657 // And iterate through them, adding the icon data to the new icon database |
|
658 while ((url = [enumerator nextObject]) != nil) { |
|
659 iconData = iconDataFromPathForIconURL(databaseDirectory, url); |
|
660 if (iconData) |
|
661 iconDatabase()->importIconDataForIconURL(SharedBuffer::wrapNSData(iconData), url); |
|
662 else { |
|
663 // This really *shouldn't* happen, so it'd be good to track down why it might happen in a debug build |
|
664 // however, we do know how to handle it gracefully in release |
|
665 LOG_ERROR("%@ is marked as having an icon on disk, but we couldn't get the data for it", url); |
|
666 iconDatabase()->importIconDataForIconURL(0, url); |
|
667 } |
|
668 if (iconDatabase()->shouldStopThreadActivity()) |
|
669 return false; |
|
670 } |
|
671 |
|
672 // After we're done importing old style icons over to webcore icons, we delete the entire directory hierarchy |
|
673 // for the old icon DB (skipping the new iconDB if it is in the same directory) |
|
674 NSFileManager *fileManager = [NSFileManager defaultManager]; |
|
675 enumerator = [[fileManager contentsOfDirectoryAtPath:databaseDirectory error:NULL] objectEnumerator]; |
|
676 |
|
677 NSString *databaseFilename = iconDatabase()->defaultDatabaseFilename(); |
|
678 |
|
679 BOOL foundIconDB = NO; |
|
680 NSString *file; |
|
681 while ((file = [enumerator nextObject]) != nil) { |
|
682 if ([file caseInsensitiveCompare:databaseFilename] == NSOrderedSame) { |
|
683 foundIconDB = YES; |
|
684 continue; |
|
685 } |
|
686 NSString *filePath = [databaseDirectory stringByAppendingPathComponent:file]; |
|
687 if (![fileManager removeItemAtPath:filePath error:NULL]) |
|
688 LOG_ERROR("Failed to delete %@ from old icon directory", filePath); |
|
689 } |
|
690 |
|
691 // If the new iconDB wasn't in that directory, we can delete the directory itself |
|
692 if (!foundIconDB) |
|
693 rmdir([databaseDirectory fileSystemRepresentation]); |
|
694 |
|
695 return true; |
|
696 } |
|
697 |
|
698 NSImage *webGetNSImage(Image* image, NSSize size) |
|
699 { |
|
700 ASSERT_MAIN_THREAD(); |
|
701 ASSERT(size.width); |
|
702 ASSERT(size.height); |
|
703 |
|
704 // FIXME: We're doing the resize here for now because WebCore::Image doesn't yet support resizing/multiple representations |
|
705 // This makes it so there's effectively only one size of a particular icon in the system at a time. We should move this |
|
706 // to WebCore::Image at some point. |
|
707 if (!image) |
|
708 return nil; |
|
709 NSImage* nsImage = image->getNSImage(); |
|
710 if (!nsImage) |
|
711 return nil; |
|
712 if (!NSEqualSizes([nsImage size], size)) { |
|
713 [nsImage setScalesWhenResized:YES]; |
|
714 [nsImage setSize:size]; |
|
715 } |
|
716 return nsImage; |
|
717 } |