123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- /*
- * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #import "WebIconDatabaseInternal.h"
- #import "WebIconDatabaseClient.h"
- #import "WebIconDatabaseDelegate.h"
- #import "WebKitLogging.h"
- #import "WebKitNSStringExtras.h"
- #import "WebNSFileManagerExtras.h"
- #import "WebNSNotificationCenterExtras.h"
- #import "WebNSURLExtras.h"
- #import "WebPreferencesPrivate.h"
- #import "WebTypesInternal.h"
- #import <WebCore/IconDatabase.h>
- #import <WebCore/Image.h>
- #import <WebCore/IntSize.h>
- #import <WebCore/RunLoop.h>
- #import <WebCore/SharedBuffer.h>
- #import <WebCore/ThreadCheck.h>
- #import <runtime/InitializeThreading.h>
- #import <wtf/MainThread.h>
- using namespace WebCore;
- NSString * const WebIconDatabaseVersionKey = @"WebIconDatabaseVersion";
- NSString * const WebURLToIconURLKey = @"WebSiteURLToIconURLKey";
- NSString *WebIconDatabaseDidAddIconNotification = @"WebIconDatabaseDidAddIconNotification";
- NSString *WebIconNotificationUserInfoURLKey = @"WebIconNotificationUserInfoURLKey";
- NSString *WebIconDatabaseDidRemoveAllIconsNotification = @"WebIconDatabaseDidRemoveAllIconsNotification";
- NSString *WebIconDatabaseDirectoryDefaultsKey = @"WebIconDatabaseDirectoryDefaultsKey";
- NSString *WebIconDatabaseEnabledDefaultsKey = @"WebIconDatabaseEnabled";
- NSString *WebIconDatabasePath = @"~/Library/Icons";
- NSSize WebIconSmallSize = {16, 16};
- NSSize WebIconMediumSize = {32, 32};
- NSSize WebIconLargeSize = {128, 128};
- #define UniqueFilePathSize (34)
- static WebIconDatabaseClient* defaultClient()
- {
- #if ENABLE(ICONDATABASE)
- static WebIconDatabaseClient* defaultClient = new WebIconDatabaseClient();
- return defaultClient;
- #else
- return 0;
- #endif
- }
- @interface WebIconDatabase (WebReallyInternal)
- - (void)_sendNotificationForURL:(NSString *)URL;
- - (void)_sendDidRemoveAllIconsNotification;
- - (NSImage *)_iconForFileURL:(NSString *)fileURL withSize:(NSSize)size;
- - (void)_resetCachedWebPreferences:(NSNotification *)notification;
- - (NSImage *)_largestIconFromDictionary:(NSMutableDictionary *)icons;
- - (NSMutableDictionary *)_iconsBySplittingRepresentationsOfIcon:(NSImage *)icon;
- - (NSImage *)_iconFromDictionary:(NSMutableDictionary *)icons forSize:(NSSize)size cache:(BOOL)cache;
- - (void)_scaleIcon:(NSImage *)icon toSize:(NSSize)size;
- - (NSString *)_databaseDirectory;
- @end
- @implementation WebIconDatabase
- + (void)initialize
- {
- JSC::initializeThreading();
- WTF::initializeMainThreadToProcessMainThread();
- WebCore::RunLoop::initializeMainRunLoop();
- }
- + (WebIconDatabase *)sharedIconDatabase
- {
- static WebIconDatabase *database = nil;
- if (!database)
- database = [[WebIconDatabase alloc] init];
- return database;
- }
- - (id)init
- {
- self = [super init];
- if (!self)
- return nil;
- WebCoreThreadViolationCheckRoundOne();
-
- _private = [[WebIconDatabasePrivate alloc] init];
-
- // Check the user defaults and see if the icon database should even be enabled.
- // Inform the bridge and, if we're disabled, bail from init right here
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
- // <rdar://problem/4741419> - IconDatabase should be disabled by default
- NSDictionary *initialDefaults = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithBool:YES], WebIconDatabaseEnabledDefaultsKey, nil];
- [defaults registerDefaults:initialDefaults];
- [initialDefaults release];
- BOOL enabled = [defaults boolForKey:WebIconDatabaseEnabledDefaultsKey];
- iconDatabase().setEnabled(enabled);
- if (enabled)
- [self _startUpIconDatabase];
- return self;
- }
- - (NSImage *)iconForURL:(NSString *)URL withSize:(NSSize)size cache:(BOOL)cache
- {
- ASSERT_MAIN_THREAD();
- ASSERT(size.width);
- ASSERT(size.height);
- if (!URL || ![self isEnabled])
- return [self defaultIconForURL:URL withSize:size];
- // FIXME - <rdar://problem/4697934> - Move the handling of FileURLs to WebCore and implement in ObjC++
- if ([URL _webkit_isFileURL])
- return [self _iconForFileURL:URL withSize:size];
-
- if (Image* image = iconDatabase().synchronousIconForPageURL(URL, IntSize(size)))
- if (NSImage *icon = webGetNSImage(image, size))
- return icon;
- return [self defaultIconForURL:URL withSize:size];
- }
- - (NSImage *)iconForURL:(NSString *)URL withSize:(NSSize)size
- {
- return [self iconForURL:URL withSize:size cache:YES];
- }
- - (NSString *)iconURLForURL:(NSString *)URL
- {
- if (![self isEnabled])
- return nil;
- ASSERT_MAIN_THREAD();
- return iconDatabase().synchronousIconURLForPageURL(URL);
- }
- - (NSImage *)defaultIconWithSize:(NSSize)size
- {
- ASSERT_MAIN_THREAD();
- ASSERT(size.width);
- ASSERT(size.height);
-
- Image* image = iconDatabase().defaultIcon(IntSize(size));
- return image ? image->getNSImage() : nil;
- }
- - (NSImage *)defaultIconForURL:(NSString *)URL withSize:(NSSize)size
- {
- if (_private->delegateImplementsDefaultIconForURL)
- return [_private->delegate webIconDatabase:self defaultIconForURL:URL withSize:size];
- return [self defaultIconWithSize:size];
- }
- - (void)retainIconForURL:(NSString *)URL
- {
- ASSERT_MAIN_THREAD();
- ASSERT(URL);
- if (![self isEnabled])
- return;
- iconDatabase().retainIconForPageURL(URL);
- }
- - (void)releaseIconForURL:(NSString *)pageURL
- {
- ASSERT_MAIN_THREAD();
- ASSERT(pageURL);
- if (![self isEnabled])
- return;
- iconDatabase().releaseIconForPageURL(pageURL);
- }
- + (void)delayDatabaseCleanup
- {
- ASSERT_MAIN_THREAD();
- IconDatabase::delayDatabaseCleanup();
- }
- + (void)allowDatabaseCleanup
- {
- ASSERT_MAIN_THREAD();
- IconDatabase::allowDatabaseCleanup();
- }
- - (void)setDelegate:(id)delegate
- {
- _private->delegate = delegate;
- _private->delegateImplementsDefaultIconForURL = [delegate respondsToSelector:@selector(webIconDatabase:defaultIconForURL:withSize:)];
- }
- - (id)delegate
- {
- return _private->delegate;
- }
- @end
- @implementation WebIconDatabase (WebPendingPublic)
- - (BOOL)isEnabled
- {
- return iconDatabase().isEnabled();
- }
- - (void)setEnabled:(BOOL)flag
- {
- BOOL currentlyEnabled = [self isEnabled];
- if (currentlyEnabled && !flag) {
- iconDatabase().setEnabled(false);
- [self _shutDownIconDatabase];
- } else if (!currentlyEnabled && flag) {
- iconDatabase().setEnabled(true);
- [self _startUpIconDatabase];
- }
- }
- - (void)removeAllIcons
- {
- ASSERT_MAIN_THREAD();
- if (![self isEnabled])
- return;
- // Via the IconDatabaseClient interface, removeAllIcons() will send the WebIconDatabaseDidRemoveAllIconsNotification
- iconDatabase().removeAllIcons();
- }
- @end
- @implementation WebIconDatabase (WebPrivate)
- + (void)_checkIntegrityBeforeOpening
- {
- IconDatabase::checkIntegrityBeforeOpening();
- }
- @end
- @implementation WebIconDatabase (WebInternal)
- - (void)_sendNotificationForURL:(NSString *)URL
- {
- ASSERT(URL);
-
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:URL
- forKey:WebIconNotificationUserInfoURLKey];
-
- [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:WebIconDatabaseDidAddIconNotification
- object:self
- userInfo:userInfo];
- }
- - (void)_sendDidRemoveAllIconsNotification
- {
- [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:WebIconDatabaseDidRemoveAllIconsNotification
- object:self
- userInfo:nil];
- }
- - (void)_startUpIconDatabase
- {
- iconDatabase().setClient(defaultClient());
-
- // Figure out the directory we should be using for the icon.db
- NSString *databaseDirectory = [self _databaseDirectory];
-
- // Rename legacy icon database files to the new icon database name
- BOOL isDirectory = NO;
- NSString *legacyDB = [databaseDirectory stringByAppendingPathComponent:@"icon.db"];
- NSFileManager *defaultManager = [NSFileManager defaultManager];
- if ([defaultManager fileExistsAtPath:legacyDB isDirectory:&isDirectory] && !isDirectory) {
- NSString *newDB = [databaseDirectory stringByAppendingPathComponent:IconDatabase::defaultDatabaseFilename()];
- if (![defaultManager fileExistsAtPath:newDB])
- rename([legacyDB fileSystemRepresentation], [newDB fileSystemRepresentation]);
- }
-
- // Set the private browsing pref then open the WebCore icon database
- iconDatabase().setPrivateBrowsingEnabled([[WebPreferences standardPreferences] privateBrowsingEnabled]);
- if (!iconDatabase().open(databaseDirectory, IconDatabase::defaultDatabaseFilename()))
- LOG_ERROR("Unable to open icon database");
-
- // Register for important notifications
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(_applicationWillTerminate:)
- name:NSApplicationWillTerminateNotification
- object:NSApp];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(_resetCachedWebPreferences:)
- name:WebPreferencesChangedInternalNotification
- object:nil];
- }
- - (void)_shutDownIconDatabase
- {
- // Unregister for important notifications
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:NSApplicationWillTerminateNotification
- object:NSApp];
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:WebPreferencesChangedInternalNotification
- object:nil];
- }
- - (void)_applicationWillTerminate:(NSNotification *)notification
- {
- iconDatabase().close();
- }
- - (NSImage *)_iconForFileURL:(NSString *)file withSize:(NSSize)size
- {
- ASSERT_MAIN_THREAD();
- ASSERT(size.width);
- ASSERT(size.height);
- NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
- NSString *path = [[NSURL _web_URLWithDataAsString:file] path];
- NSString *suffix = [path pathExtension];
- NSImage *icon = nil;
-
- if ([suffix _webkit_isCaseInsensitiveEqualToString:@"htm"] || [suffix _webkit_isCaseInsensitiveEqualToString:@"html"]) {
- if (!_private->htmlIcons) {
- icon = [workspace iconForFileType:@"html"];
- _private->htmlIcons = [[self _iconsBySplittingRepresentationsOfIcon:icon] retain];
- }
- icon = [self _iconFromDictionary:_private->htmlIcons forSize:size cache:YES];
- } else {
- if (!path || ![path isAbsolutePath]) {
- // Return the generic icon when there is no path.
- icon = [workspace iconForFileType:NSFileTypeForHFSTypeCode(kGenericDocumentIcon)];
- } else {
- icon = [workspace iconForFile:path];
- }
- [self _scaleIcon:icon toSize:size];
- }
- return icon;
- }
- - (void)_resetCachedWebPreferences:(NSNotification *)notification
- {
- BOOL privateBrowsingEnabledNow = [[WebPreferences standardPreferences] privateBrowsingEnabled];
- iconDatabase().setPrivateBrowsingEnabled(privateBrowsingEnabledNow);
- }
- - (NSImage *)_largestIconFromDictionary:(NSMutableDictionary *)icons
- {
- ASSERT(icons);
-
- NSEnumerator *enumerator = [icons keyEnumerator];
- NSValue *currentSize, *largestSize=nil;
- float largestSizeArea=0;
- while ((currentSize = [enumerator nextObject]) != nil) {
- NSSize currentSizeSize = [currentSize sizeValue];
- float currentSizeArea = currentSizeSize.width * currentSizeSize.height;
- if(!largestSizeArea || (currentSizeArea > largestSizeArea)){
- largestSize = currentSize;
- largestSizeArea = currentSizeArea;
- }
- }
- return [icons objectForKey:largestSize];
- }
- - (NSMutableDictionary *)_iconsBySplittingRepresentationsOfIcon:(NSImage *)icon
- {
- ASSERT(icon);
- NSMutableDictionary *icons = [NSMutableDictionary dictionary];
- NSEnumerator *enumerator = [[icon representations] objectEnumerator];
- NSImageRep *rep;
- while ((rep = [enumerator nextObject]) != nil) {
- NSSize size = [rep size];
- NSImage *subIcon = [[NSImage alloc] initWithSize:size];
- [subIcon addRepresentation:rep];
- [icons setObject:subIcon forKey:[NSValue valueWithSize:size]];
- [subIcon release];
- }
- if([icons count] > 0)
- return icons;
- LOG_ERROR("icon has no representations");
-
- return nil;
- }
- - (NSImage *)_iconFromDictionary:(NSMutableDictionary *)icons forSize:(NSSize)size cache:(BOOL)cache
- {
- ASSERT(size.width);
- ASSERT(size.height);
- NSImage *icon = [icons objectForKey:[NSValue valueWithSize:size]];
- if(!icon){
- icon = [[[self _largestIconFromDictionary:icons] copy] autorelease];
- [self _scaleIcon:icon toSize:size];
- if(cache){
- [icons setObject:icon forKey:[NSValue valueWithSize:size]];
- }
- }
- return icon;
- }
- - (void)_scaleIcon:(NSImage *)icon toSize:(NSSize)size
- {
- ASSERT(size.width);
- ASSERT(size.height);
-
- #if !LOG_DISABLED
- double start = CFAbsoluteTimeGetCurrent();
- #endif
-
- [icon setScalesWhenResized:YES];
- [icon setSize:size];
-
- #if !LOG_DISABLED
- double duration = CFAbsoluteTimeGetCurrent() - start;
- LOG(Timing, "scaling icon took %f seconds.", duration);
- #endif
- }
- - (NSString *)_databaseDirectory
- {
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
- // Figure out the directory we should be using for the icon.db
- NSString *databaseDirectory = [defaults objectForKey:WebIconDatabaseDirectoryDefaultsKey];
- if (!databaseDirectory) {
- databaseDirectory = WebIconDatabasePath;
- [defaults setObject:databaseDirectory forKey:WebIconDatabaseDirectoryDefaultsKey];
- }
-
- return [[databaseDirectory stringByExpandingTildeInPath] stringByStandardizingPath];
- }
- @end
- @implementation WebIconDatabasePrivate
- @end
- NSImage *webGetNSImage(Image* image, NSSize size)
- {
- ASSERT_MAIN_THREAD();
- ASSERT(size.width);
- ASSERT(size.height);
- // FIXME: We're doing the resize here for now because WebCore::Image doesn't yet support resizing/multiple representations
- // This makes it so there's effectively only one size of a particular icon in the system at a time. We should move this
- // to WebCore::Image at some point.
- if (!image)
- return nil;
- NSImage* nsImage = image->getNSImage();
- if (!nsImage)
- return nil;
- if (!NSEqualSizes([nsImage size], size)) {
- [nsImage setScalesWhenResized:YES];
- [nsImage setSize:size];
- }
- return nsImage;
- }
|