mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-29 03:44:37 +00:00
Add site icons to the history outliner and history menus.
Add support for site icons specified in link elements (bug 162893), part of patch by Ludovic Hirlimann. Add a site icon image cache, so that we can get site icons without a round trip through necko, and avoid duplicate images (bug 294675). Add a Clear History item to the end of the go menu (bug 294205). Make the history menus no more than 100 items long, with a "Show More" item at the end, to avoid long delays when showing them (bug 291414).
This commit is contained in:
parent
62ece01069
commit
161e295483
Binary file not shown.
@ -140,6 +140,7 @@ typedef enum EBookmarkOpenBehavior
|
||||
-(IBAction) viewSource:(id)aSender;
|
||||
-(IBAction) manageBookmarks: (id)aSender;
|
||||
-(IBAction) showHistory:(id)aSender;
|
||||
-(IBAction) clearHistory:(id)aSender;
|
||||
-(IBAction) reloadWithCharset:(id)aSender;
|
||||
|
||||
// Bookmarks menu actions.
|
||||
|
@ -1079,6 +1079,26 @@ Otherwise, we return the URL we originally got. Right now this supports .url and
|
||||
[[browserWindow windowController] manageHistory: aSender];
|
||||
}
|
||||
|
||||
//
|
||||
// -clearHistory:
|
||||
//
|
||||
// clear the global history, after showing a warning
|
||||
//
|
||||
-(IBAction)clearHistory:(id)aSender
|
||||
{
|
||||
if (NSRunCriticalAlertPanel(NSLocalizedString(@"ClearHistoryTitle", nil),
|
||||
NSLocalizedString(@"ClearHistoryMessage", nil),
|
||||
NSLocalizedString(@"ClearHistoryButton", nil),
|
||||
NSLocalizedString(@"CancelButtonText", nil),
|
||||
nil) == NSAlertDefaultReturn)
|
||||
{
|
||||
// clear history
|
||||
nsCOMPtr<nsIBrowserHistory> hist = do_GetService("@mozilla.org/browser/global-history;2");
|
||||
if (hist)
|
||||
hist->RemoveAllPages();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// manageBookmarks:
|
||||
//
|
||||
|
@ -177,8 +177,16 @@ NSString* const URLLoadSuccessKey = @"url_bool";
|
||||
|
||||
-(void) refreshIcon
|
||||
{
|
||||
if ([BookmarkManager sharedBookmarkManager]) {
|
||||
[[SiteIconProvider sharedFavoriteIconProvider] loadFavoriteIcon:self forURI:[self url] allowNetwork:NO];
|
||||
if ([BookmarkManager sharedBookmarkManager])
|
||||
{
|
||||
NSImage* siteIcon = [[SiteIconProvider sharedFavoriteIconProvider] favoriteIconForPage:[self url]];
|
||||
if (siteIcon)
|
||||
[self setIcon:siteIcon];
|
||||
else
|
||||
[[SiteIconProvider sharedFavoriteIconProvider] fetchFavoriteIconForPage:[self url]
|
||||
withIconLocation:nil
|
||||
allowNetwork:NO
|
||||
notifyingClient:self];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -697,7 +697,7 @@ static const int kDisabledQuicksearchPopupItemTag = 9999;
|
||||
return mBookmarksEditingView;
|
||||
}
|
||||
|
||||
- (void) focus
|
||||
- (void)focus
|
||||
{
|
||||
[[mBookmarksOutlineView window] makeFirstResponder:mBookmarksOutlineView];
|
||||
|
||||
@ -706,7 +706,8 @@ static const int kDisabledQuicksearchPopupItemTag = 9999;
|
||||
// manager view resized correctly. If we did it earlier, it would resize again
|
||||
// to stretch proportionally to the size of the browser window, destroying
|
||||
// the width we just set.
|
||||
if (!mSplittersRestored) {
|
||||
if (!mSplittersRestored)
|
||||
{
|
||||
const float kDefaultSplitWidth = kMinContainerSplitWidth;
|
||||
float savedWidth = [[NSUserDefaults standardUserDefaults] floatForKey:USER_DEFAULTS_CONTAINER_SPLITTER_WIDTH];
|
||||
if (savedWidth < kDefaultSplitWidth)
|
||||
|
@ -1557,6 +1557,11 @@ enum BWCOpenDest {
|
||||
-(IBAction)manageHistory: (id)aSender
|
||||
{
|
||||
[self loadURL:@"about:history" referrer:nil activate:YES allowPopups:YES];
|
||||
|
||||
// aSender could be a history menu item with a represented object of
|
||||
// an item that we wish to reveal. However, it belongs to a different
|
||||
// data source than the one we just created. need a way to find the one
|
||||
// to reveal...
|
||||
}
|
||||
|
||||
- (IBAction)goToLocationFromToolbarURLField:(id)sender
|
||||
|
@ -480,18 +480,37 @@ static NSString* const kOfflineNotificationName = @"offlineModeChanged";
|
||||
|
||||
- (void)onLocationChange:(NSString*)urlSpec
|
||||
{
|
||||
BOOL useSiteIcons = [[PreferenceManager sharedInstance] getBooleanPref:"browser.chrome.site_icons" withSuccess:NULL];
|
||||
BOOL useSiteIcons = [[PreferenceManager sharedInstance] getBooleanPref:"browser.chrome.favicons" withSuccess:NULL];
|
||||
BOOL siteIconLoadInitiated = NO;
|
||||
|
||||
SiteIconProvider* faviconProvider = [SiteIconProvider sharedFavoriteIconProvider];
|
||||
NSString* faviconURI = [SiteIconProvider faviconLocationStringFromURI:urlSpec];
|
||||
|
||||
|
||||
if (useSiteIcons && [faviconURI length] > 0)
|
||||
{
|
||||
// if the favicon uri has changed, fire off favicon load. When it completes, our
|
||||
// imageLoadedNotification selector gets called.
|
||||
if (![faviconURI isEqualToString:mSiteIconURI])
|
||||
siteIconLoadInitiated = [faviconProvider loadFavoriteIcon:self forURI:urlSpec allowNetwork:YES];
|
||||
{
|
||||
// first get a cached image for this site, if we have one. we'll go ahead
|
||||
// and request the load anyway, in case the site updated their icon.
|
||||
NSImage* cachedImage = [faviconProvider favoriteIconForPage:urlSpec];
|
||||
NSString* cachedImageURI = nil;
|
||||
|
||||
if (cachedImage)
|
||||
cachedImageURI = faviconURI;
|
||||
|
||||
// immediately update the site icon (to the cached one, or the default)
|
||||
[self updateSiteIconImage:cachedImage withURI:cachedImageURI];
|
||||
|
||||
// note that this is the only time we hit the network for site icons.
|
||||
// note also that we may get a site icon from a link element later,
|
||||
// which will replace any we get from the default location.
|
||||
[faviconProvider fetchFavoriteIconForPage:urlSpec
|
||||
withIconLocation:nil
|
||||
allowNetwork:YES
|
||||
notifyingClient:self];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -499,13 +518,13 @@ static NSString* const kOfflineNotificationName = @"offlineModeChanged";
|
||||
faviconURI = urlSpec;
|
||||
else
|
||||
faviconURI = @"";
|
||||
|
||||
[self updateSiteIconImage:nil withURI:faviconURI];
|
||||
}
|
||||
|
||||
if (!siteIconLoadInitiated)
|
||||
[self updateSiteIconImage:nil withURI:faviconURI];
|
||||
|
||||
[mDelegate updateLocationFields:urlSpec ignoreTyping:NO];
|
||||
|
||||
// see if someone wants to replace the main view
|
||||
[self checkForCustomViewOnLoad:urlSpec];
|
||||
}
|
||||
|
||||
@ -648,6 +667,27 @@ static NSString* const kOfflineNotificationName = @"offlineModeChanged";
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a "shortcut icon" link element is noticed
|
||||
- (void)onFoundShortcutIcon:(NSString*)inIconURI
|
||||
{
|
||||
BOOL useSiteIcons = [[PreferenceManager sharedInstance] getBooleanPref:"browser.chrome.favicons" withSuccess:NULL];
|
||||
if (!useSiteIcons)
|
||||
return;
|
||||
|
||||
if ([inIconURI length] > 0)
|
||||
{
|
||||
// if the favicon uri has changed, fire off favicon load. When it completes, our
|
||||
// imageLoadedNotification selector gets called.
|
||||
if (![inIconURI isEqualToString:mSiteIconURI])
|
||||
{
|
||||
[[SiteIconProvider sharedFavoriteIconProvider] fetchFavoriteIconForPage:[self getCurrentURLSpec]
|
||||
withIconLocation:inIconURI
|
||||
allowNetwork:YES
|
||||
notifyingClient:self];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a context menu should be shown.
|
||||
- (void)onShowContextMenu:(int)flags domEvent:(nsIDOMEvent*)aEvent domNode:(nsIDOMNode*)aNode
|
||||
{
|
||||
@ -838,8 +878,8 @@ static NSString* const kOfflineNotificationName = @"offlineModeChanged";
|
||||
siteIcon = [NSImage imageNamed:@"globe_ico"];
|
||||
}
|
||||
|
||||
[self setSiteIconImage: siteIcon];
|
||||
[self setSiteIconURI: inSiteIconURI];
|
||||
[self setSiteIconImage:siteIcon];
|
||||
[self setSiteIconURI:inSiteIconURI];
|
||||
|
||||
// update the proxy icon
|
||||
[mDelegate updateSiteIcons:mSiteIconImage ignoreTyping:NO];
|
||||
|
@ -68,6 +68,8 @@
|
||||
- (void)setItemBeforeHistoryItems:(NSMenuItem*)inItem;
|
||||
- (NSMenuItem*)itemBeforeHistoryItems;
|
||||
|
||||
- (void)addLastItems;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
@ -64,7 +64,7 @@
|
||||
|
||||
|
||||
// the maximum number of history entry menuitems to display
|
||||
static const int kMaxItems = 15;
|
||||
static const int kMaxNumHistoryItems = 100;
|
||||
|
||||
// the maximum number of "today" items to show on the main menu
|
||||
static const int kMaxTodayItems = 12;
|
||||
@ -301,15 +301,16 @@ static const unsigned int kMaxTitleLength = 80;
|
||||
{
|
||||
NSMenuItem* newItem = nil;
|
||||
|
||||
// XXX should we impose a max number of items in any one menu, to avoid crazy 2000-item menus?
|
||||
if ([curChild isKindOfClass:[HistorySiteItem class]])
|
||||
{
|
||||
newItem = [[[NSMenuItem alloc] initWithTitle:[HistoryMenu menuItemTitleForHistoryItem:curChild]
|
||||
action:@selector(openHistoryItem:)
|
||||
keyEquivalent:@""] autorelease];
|
||||
|
||||
[newItem setImage:[curChild iconAllowingLoad:NO]];
|
||||
[newItem setTarget:self];
|
||||
[newItem setRepresentedObject:curChild];
|
||||
[self addItem:newItem];
|
||||
}
|
||||
else if ([curChild isKindOfClass:[HistoryCategoryItem class]] && ([curChild numberOfChildren] > 0))
|
||||
{
|
||||
@ -321,6 +322,7 @@ static const unsigned int kMaxTitleLength = 80;
|
||||
[mAdditionalItemsParent autorelease];
|
||||
mAdditionalItemsParent = [curChild retain];
|
||||
|
||||
// put the kMaxTodayItems most recent items into this menu (which is the go menu)
|
||||
NSMutableArray* menuTodayItems = [NSMutableArray arrayWithArray:[curChild children]];
|
||||
while ((int)[menuTodayItems count] > kMaxTodayItems)
|
||||
[menuTodayItems removeObjectAtIndex:kMaxTodayItems];
|
||||
@ -333,13 +335,14 @@ static const unsigned int kMaxTitleLength = 80;
|
||||
action:@selector(openHistoryItem:)
|
||||
keyEquivalent:@""] autorelease];
|
||||
|
||||
[todayMenuItem setImage:[curTodayItem iconAllowingLoad:NO]];
|
||||
[todayMenuItem setTarget:self];
|
||||
[todayMenuItem setRepresentedObject:curTodayItem];
|
||||
[self addItem:todayMenuItem];
|
||||
separatorPending = YES;
|
||||
}
|
||||
|
||||
// make a submenu for "earlier today"
|
||||
// and make a submenu for "earlier today"
|
||||
if ([curChild numberOfChildren] > kMaxTodayItems)
|
||||
{
|
||||
if (separatorPending)
|
||||
@ -352,12 +355,14 @@ static const unsigned int kMaxTitleLength = 80;
|
||||
newItem = [[[NSMenuItem alloc] initWithTitle:itemTitle
|
||||
action:nil
|
||||
keyEquivalent:@""] autorelease];
|
||||
[newItem setImage:[curChild iconAllowingLoad:NO]];
|
||||
|
||||
HistoryMenu* newSubmenu = [[HistoryMenu alloc] initWithTitle:itemTitle];
|
||||
[newSubmenu setRootHistoryItem:curChild];
|
||||
[newSubmenu setNumLeadingItemsToIgnore:kMaxTodayItems];
|
||||
|
||||
[newItem setSubmenu:newSubmenu];
|
||||
[self addItem:newItem];
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -373,21 +378,40 @@ static const unsigned int kMaxTitleLength = 80;
|
||||
newItem = [[[NSMenuItem alloc] initWithTitle:itemTitle
|
||||
action:nil
|
||||
keyEquivalent:@""] autorelease];
|
||||
[newItem setImage:[curChild iconAllowingLoad:NO]];
|
||||
|
||||
HistoryMenu* newSubmenu = [[HistoryMenu alloc] initWithTitle:itemTitle];
|
||||
[newSubmenu setRootHistoryItem:curChild];
|
||||
|
||||
[newItem setSubmenu:newSubmenu];
|
||||
[self addItem:newItem];
|
||||
}
|
||||
}
|
||||
|
||||
if (newItem)
|
||||
[self addItem:newItem];
|
||||
// if we're not the Go menu, stop after kMaxNumHistoryItems items
|
||||
if (![self isKindOfClass:[GoMenu class]] && ([self numberOfItems] == kMaxNumHistoryItems))
|
||||
break;
|
||||
}
|
||||
|
||||
[self addLastItems];
|
||||
|
||||
mHistoryItemsDirty = NO;
|
||||
}
|
||||
|
||||
- (void)addLastItems
|
||||
{
|
||||
if ([self numberOfItems] >= kMaxNumHistoryItems)
|
||||
{
|
||||
// this will only be called for submenus
|
||||
[self addItem:[NSMenuItem separatorItem]];
|
||||
NSMenuItem* showMoreItem = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"ShowMoreHistoryMenuItem", @"")
|
||||
action:@selector(showHistory:)
|
||||
keyEquivalent:@""] autorelease];
|
||||
[showMoreItem setRepresentedObject:mRootItem];
|
||||
[self addItem:showMoreItem];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)menuWillBeDisplayed
|
||||
{
|
||||
[self rebuildHistoryItems];
|
||||
@ -478,4 +502,16 @@ static const unsigned int kMaxTitleLength = 80;
|
||||
[super menuWillBeDisplayed];
|
||||
}
|
||||
|
||||
- (void)addLastItems
|
||||
{
|
||||
// at the bottom of the go menu, add a Clear History item
|
||||
if ([[mRootItem children] count] > 0)
|
||||
[self addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
NSMenuItem* clearHistoryItem = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"ClearHistoryMenuItem", @"")
|
||||
action:@selector(clearHistory:)
|
||||
keyEquivalent:@""] autorelease];
|
||||
[self addItem:clearHistoryItem];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -38,11 +38,11 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
extern NSString* RemoteDataLoadRequestNotificationName;
|
||||
extern NSString* RemoteDataLoadRequestURIKey;
|
||||
extern NSString* RemoteDataLoadRequestDataKey;
|
||||
extern NSString* RemoteDataLoadRequestUserDataKey;
|
||||
extern NSString* RemoteDataLoadRequestResultKey;
|
||||
extern NSString* const RemoteDataLoadRequestNotificationName;
|
||||
extern NSString* const RemoteDataLoadRequestURIKey;
|
||||
extern NSString* const RemoteDataLoadRequestDataKey;
|
||||
extern NSString* const RemoteDataLoadRequestUserDataKey;
|
||||
extern NSString* const RemoteDataLoadRequestResultKey;
|
||||
|
||||
// RemoteDataProvider is a class that can be used to do asynchronous loads
|
||||
// from URIs using necko, and passing back the result of the load to a
|
||||
|
@ -47,11 +47,11 @@
|
||||
#include "nsICacheEntryDescriptor.h"
|
||||
#include "nsICachingChannel.h"
|
||||
|
||||
NSString* RemoteDataLoadRequestNotificationName = @"remoteload_notification_name";
|
||||
NSString* RemoteDataLoadRequestURIKey = @"remoteload_uri_key";
|
||||
NSString* RemoteDataLoadRequestDataKey = @"remoteload_data_key";
|
||||
NSString* RemoteDataLoadRequestUserDataKey = @"remoteload_user_data_key";
|
||||
NSString* RemoteDataLoadRequestResultKey = @"remoteload_result_key";
|
||||
NSString* const RemoteDataLoadRequestNotificationName = @"remoteload_notification_name";
|
||||
NSString* const RemoteDataLoadRequestURIKey = @"remoteload_uri_key";
|
||||
NSString* const RemoteDataLoadRequestDataKey = @"remoteload_data_key";
|
||||
NSString* const RemoteDataLoadRequestUserDataKey = @"remoteload_user_data_key";
|
||||
NSString* const RemoteDataLoadRequestResultKey = @"remoteload_result_key";
|
||||
|
||||
|
||||
// this has to retain the load listener, to ensure that the listener lives long
|
||||
|
@ -48,8 +48,10 @@ class NeckoCacheHelper;
|
||||
|
||||
@interface SiteIconProvider : NSObject<RemoteLoadListener>
|
||||
{
|
||||
NeckoCacheHelper* mMissedIconsCacheHelper;
|
||||
NSMutableDictionary *mRequestDict;
|
||||
NeckoCacheHelper* mIconsCacheHelper;
|
||||
NSMutableDictionary* mRequestDict; // dict of favicon url -> request url
|
||||
|
||||
NSMutableDictionary* mIconDictionary; // map of favorite url -> NSImage
|
||||
}
|
||||
|
||||
+ (SiteIconProvider*)sharedFavoriteIconProvider;
|
||||
@ -68,4 +70,27 @@ class NeckoCacheHelper;
|
||||
|
||||
- (BOOL)loadFavoriteIcon:(id)sender forURI:(NSString *)inURI allowNetwork:(BOOL)inAllowNetwork;
|
||||
|
||||
// fetch the icon for the given page.
|
||||
// inIconURI is the URI of the icon (if specified via a <link> element), or nil for the default
|
||||
// site icon location.
|
||||
// when the load is done, a SiteIconLoadNotificationName notification will be sent with
|
||||
// |inClient| as the object.
|
||||
// returns YES if the load is initiated, and the client can expect a notification.
|
||||
//
|
||||
// there are various reasons why this might fail to load a site icon from the cache,
|
||||
// when we know we can load it if we hit the network:
|
||||
// i) it's a https URL, which is never put in the disk cache.
|
||||
// ii) the url might have moved (301 response), and we don't handle that.
|
||||
- (BOOL)fetchFavoriteIconForPage:(NSString*)inPageURI
|
||||
withIconLocation:(NSString*)inIconURI
|
||||
allowNetwork:(BOOL)inAllowNetwork
|
||||
notifyingClient:(id)inClient;
|
||||
|
||||
// image cache method
|
||||
|
||||
// get the image for the given page URI. if not available, returns nil.
|
||||
// if the image was fetched with a specific location (e.g. because of a <link> element)
|
||||
// then this will take that into account.
|
||||
- (NSImage*)favoriteIconForPage:(NSString*)inPageURI;
|
||||
|
||||
@end
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "nsString.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIURL.h"
|
||||
#include "nsICacheSession.h"
|
||||
#include "nsICacheService.h"
|
||||
#include "nsICacheEntryDescriptor.h"
|
||||
@ -53,6 +54,10 @@ NSString* const SiteIconLoadImageKey = @"siteicon_load_image";
|
||||
NSString* const SiteIconLoadURIKey = @"siteicon_load_uri";
|
||||
NSString* const SiteIconLoadUserDataKey = @"siteicon_load_user_data";
|
||||
|
||||
static const char* const kMissingFaviconMetadataKey = "missing_favicon";
|
||||
static const char* const kFaviconURILocationMetadataKey = "favicon_location";
|
||||
|
||||
//#define VERBOSE_SITE_ICON_LOADING
|
||||
|
||||
static inline PRUint32 PRTimeToSeconds(PRTime t_usec)
|
||||
{
|
||||
@ -63,37 +68,53 @@ static inline PRUint32 PRTimeToSeconds(PRTime t_usec)
|
||||
LL_L2I(t_sec, t_usec);
|
||||
return t_sec;
|
||||
}
|
||||
|
||||
|
||||
// this class is used for two things:
|
||||
// 1. to store information about which sites do not have site icons,
|
||||
// so that we don't continually hit them.
|
||||
// 2. to store a mapping between page URIs and site icon URIs,
|
||||
// so that we can reload page icons specified in <link> tags
|
||||
// without having to reload the original page.
|
||||
|
||||
class NeckoCacheHelper
|
||||
{
|
||||
public:
|
||||
|
||||
NeckoCacheHelper(const char* inMetaElement, const char* inMetaValue);
|
||||
NeckoCacheHelper();
|
||||
~NeckoCacheHelper() {}
|
||||
|
||||
nsresult Init(const char* inCacheSessionName);
|
||||
nsresult ExistsInCache(const nsACString& inURI, PRBool* outExists);
|
||||
nsresult PutInCache(const nsACString& inURI, PRUint32 inExpirationTimeSeconds);
|
||||
|
||||
// get and save state about which icons are known missing. note that the URIs here
|
||||
// are URIs for the favicons themselves
|
||||
nsresult FaviconForURIIsMissing(const nsACString& inFaviconURI, PRBool* outKnownMissing);
|
||||
nsresult SetFaviconForURIIsMissing(const nsACString& inFaviconURI, PRUint32 inExpirationTimeSeconds);
|
||||
|
||||
// get and save mapping between page URI and favicon URI, to store information
|
||||
// for pages with icons specified in <link> tags. these should only be used
|
||||
// for link-specified icons, and not those in the default location, to avoid
|
||||
// unnecessary bloat.
|
||||
nsresult SaveFaviconLocationForPageURI(const nsACString& inPageURI, const nsACString& inFaviconURI, PRUint32 inExpirationTimeSeconds);
|
||||
nsresult GetFaviconLocationForPageURI(const nsACString& inPageURI, nsACString& outFaviconURI);
|
||||
|
||||
nsresult ClearCache();
|
||||
|
||||
void GetCanonicalPageURI(const nsACString& inPageURI, nsACString& outCanonicalURI);
|
||||
|
||||
protected:
|
||||
|
||||
const char* mMetaElement;
|
||||
const char* mMetaValue;
|
||||
nsCOMPtr<nsICacheSession> mCacheSession;
|
||||
|
||||
};
|
||||
|
||||
static NS_DEFINE_CID(kCacheServiceCID, NS_CACHESERVICE_CID);
|
||||
|
||||
NeckoCacheHelper::NeckoCacheHelper(const char* inMetaElement, const char* inMetaValue)
|
||||
: mMetaElement(inMetaElement)
|
||||
, mMetaValue(inMetaValue)
|
||||
NeckoCacheHelper::NeckoCacheHelper()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult NeckoCacheHelper::Init(const char* inCacheSessionName)
|
||||
nsresult
|
||||
NeckoCacheHelper::Init(const char* inCacheSessionName)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
@ -111,23 +132,31 @@ nsresult NeckoCacheHelper::Init(const char* inCacheSessionName)
|
||||
}
|
||||
|
||||
|
||||
nsresult NeckoCacheHelper::ExistsInCache(const nsACString& inURI, PRBool* outExists)
|
||||
nsresult
|
||||
NeckoCacheHelper::FaviconForURIIsMissing(const nsACString& inFaviconURI, PRBool* outKnownMissing)
|
||||
{
|
||||
*outKnownMissing = PR_FALSE;
|
||||
|
||||
NS_ASSERTION(mCacheSession, "No cache session");
|
||||
|
||||
nsCOMPtr<nsICacheEntryDescriptor> entryDesc;
|
||||
nsresult rv = mCacheSession->OpenCacheEntry(inURI, nsICache::ACCESS_READ, nsICache::NON_BLOCKING, getter_AddRefs(entryDesc));
|
||||
|
||||
*outExists = NS_SUCCEEDED(rv) && (entryDesc != NULL);
|
||||
nsresult rv = mCacheSession->OpenCacheEntry(inFaviconURI, nsICache::ACCESS_READ, nsICache::NON_BLOCKING, getter_AddRefs(entryDesc));
|
||||
if (NS_SUCCEEDED(rv) && entryDesc)
|
||||
{
|
||||
nsXPIDLCString metadataString;
|
||||
entryDesc->GetMetaDataElement(kMissingFaviconMetadataKey, getter_Copies(metadataString));
|
||||
*outKnownMissing = metadataString.EqualsLiteral("1");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult NeckoCacheHelper::PutInCache(const nsACString& inURI, PRUint32 inExpirationTimeSeconds)
|
||||
nsresult
|
||||
NeckoCacheHelper::SetFaviconForURIIsMissing(const nsACString& inFaviconURI, PRUint32 inExpirationTimeSeconds)
|
||||
{
|
||||
NS_ASSERTION(mCacheSession, "No cache session");
|
||||
|
||||
nsCOMPtr<nsICacheEntryDescriptor> entryDesc;
|
||||
nsresult rv = mCacheSession->OpenCacheEntry(inURI, nsICache::ACCESS_WRITE, nsICache::NON_BLOCKING, getter_AddRefs(entryDesc));
|
||||
nsresult rv = mCacheSession->OpenCacheEntry(inFaviconURI, nsICache::ACCESS_WRITE, nsICache::NON_BLOCKING, getter_AddRefs(entryDesc));
|
||||
if (NS_FAILED(rv) || !entryDesc) return rv;
|
||||
|
||||
nsCacheAccessMode accessMode;
|
||||
@ -138,7 +167,7 @@ nsresult NeckoCacheHelper::PutInCache(const nsACString& inURI, PRUint32 inExpira
|
||||
if (accessMode != nsICache::ACCESS_WRITE)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
entryDesc->SetMetaDataElement(mMetaElement, mMetaValue); // just set a bit of meta data.
|
||||
entryDesc->SetMetaDataElement(kMissingFaviconMetadataKey, "1"); // just set a bit of meta data.
|
||||
entryDesc->SetExpirationTime(PRTimeToSeconds(PR_Now()) + inExpirationTimeSeconds);
|
||||
|
||||
entryDesc->MarkValid();
|
||||
@ -147,7 +176,83 @@ nsresult NeckoCacheHelper::PutInCache(const nsACString& inURI, PRUint32 inExpira
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult NeckoCacheHelper::ClearCache()
|
||||
nsresult
|
||||
NeckoCacheHelper::SaveFaviconLocationForPageURI(const nsACString& inPageURI, const nsACString& inFaviconURI, PRUint32 inExpirationTimeSeconds)
|
||||
{
|
||||
NS_ASSERTION(mCacheSession, "No cache session");
|
||||
|
||||
// canonicalize the URI
|
||||
nsCAutoString canonicalPageURI;
|
||||
GetCanonicalPageURI(inPageURI, canonicalPageURI);
|
||||
|
||||
nsCOMPtr<nsICacheEntryDescriptor> entryDesc;
|
||||
nsresult rv = mCacheSession->OpenCacheEntry(canonicalPageURI, nsICache::ACCESS_WRITE, nsICache::NON_BLOCKING, getter_AddRefs(entryDesc));
|
||||
if (NS_FAILED(rv) || !entryDesc) return rv;
|
||||
|
||||
nsCacheAccessMode accessMode;
|
||||
rv = entryDesc->GetAccessGranted(&accessMode);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (accessMode != nsICache::ACCESS_WRITE)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
entryDesc->SetMetaDataElement(kFaviconURILocationMetadataKey, PromiseFlatCString(inFaviconURI).get());
|
||||
entryDesc->SetExpirationTime(PRTimeToSeconds(PR_Now()) + inExpirationTimeSeconds);
|
||||
|
||||
entryDesc->MarkValid();
|
||||
entryDesc->Close();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NeckoCacheHelper::GetFaviconLocationForPageURI(const nsACString& inPageURI, nsACString& outFaviconURI)
|
||||
{
|
||||
NS_ASSERTION(mCacheSession, "No cache session");
|
||||
|
||||
// canonicalize the URI
|
||||
nsCAutoString canonicalPageURI;
|
||||
GetCanonicalPageURI(inPageURI, canonicalPageURI);
|
||||
|
||||
nsCOMPtr<nsICacheEntryDescriptor> entryDesc;
|
||||
nsresult rv = mCacheSession->OpenCacheEntry(canonicalPageURI, nsICache::ACCESS_READ, nsICache::NON_BLOCKING, getter_AddRefs(entryDesc));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (!entryDesc) return NS_ERROR_FAILURE;
|
||||
|
||||
nsXPIDLCString faviconLocation;
|
||||
rv = entryDesc->GetMetaDataElement(kFaviconURILocationMetadataKey, getter_Copies(faviconLocation));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
outFaviconURI = faviconLocation;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
NeckoCacheHelper::GetCanonicalPageURI(const nsACString& inPageURI, nsACString& outCanonicalURI)
|
||||
{
|
||||
nsCOMPtr<nsIURI> pageURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(pageURI), inPageURI, nsnull, nsnull);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
{
|
||||
nsCOMPtr<nsIURL> pageURL = do_QueryInterface(pageURI);
|
||||
if (pageURL)
|
||||
{
|
||||
// remove any params or refs
|
||||
//pageURL->SetParam(NS_LITERAL_CSTRING("")); // this is not implemented
|
||||
pageURL->SetQuery(NS_LITERAL_CSTRING(""));
|
||||
pageURL->SetRef(NS_LITERAL_CSTRING(""));
|
||||
|
||||
pageURL->GetSpec(outCanonicalURI);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
outCanonicalURI = inPageURI;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NeckoCacheHelper::ClearCache()
|
||||
{
|
||||
NS_ASSERTION(mCacheSession, "No cache session");
|
||||
|
||||
@ -157,7 +262,8 @@ nsresult NeckoCacheHelper::ClearCache()
|
||||
|
||||
#pragma mark -
|
||||
|
||||
static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& outFaviconURI)
|
||||
static nsresult
|
||||
MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& outFaviconURI)
|
||||
{
|
||||
outFaviconURI.Truncate(0);
|
||||
|
||||
@ -184,8 +290,10 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o
|
||||
|
||||
@interface SiteIconProvider(Private)
|
||||
|
||||
- (void)addToMissedIconsCache:(const nsAString&)inURI withExpirationSeconds:(unsigned int)inExpSeconds;
|
||||
- (BOOL)inMissedIconsCache:(const nsAString&)inURI;
|
||||
- (NSString*)favoriteIconURLFromPageURL:(NSString*)inPageURL;
|
||||
|
||||
- (void)addToMissedIconsCache:(NSString*)inURI withExpirationSeconds:(unsigned int)inExpSeconds;
|
||||
- (BOOL)inMissedIconsCache:(NSString*)inURI;
|
||||
|
||||
@end
|
||||
|
||||
@ -196,12 +304,14 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
mMissedIconsCacheHelper = new NeckoCacheHelper("Favicon", "Missed");
|
||||
mRequestDict = [[NSMutableDictionary alloc] initWithCapacity:5];
|
||||
nsresult rv = mMissedIconsCacheHelper->Init("MissedIconsCache");
|
||||
mRequestDict = [[NSMutableDictionary alloc] initWithCapacity:5];
|
||||
mIconDictionary = [[NSMutableDictionary alloc] initWithCapacity:100];
|
||||
|
||||
mIconsCacheHelper = new NeckoCacheHelper();
|
||||
nsresult rv = mIconsCacheHelper->Init("MissedIconsCache");
|
||||
if (NS_FAILED(rv)) {
|
||||
delete mMissedIconsCacheHelper;
|
||||
mMissedIconsCacheHelper = NULL;
|
||||
delete mIconsCacheHelper;
|
||||
mIconsCacheHelper = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,52 +320,60 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
delete mMissedIconsCacheHelper;
|
||||
delete mIconsCacheHelper;
|
||||
|
||||
[mRequestDict release];
|
||||
[mIconDictionary release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)addToMissedIconsCache:(const nsAString&)inURI withExpirationSeconds:(unsigned int)inExpSeconds
|
||||
- (NSString*)favoriteIconURLFromPageURL:(NSString*)inPageURL
|
||||
{
|
||||
if (mMissedIconsCacheHelper)
|
||||
NSString* faviconURL = nil;
|
||||
|
||||
// do we have a link icon for this page?
|
||||
if (mIconsCacheHelper)
|
||||
{
|
||||
mMissedIconsCacheHelper->PutInCache(NS_ConvertUCS2toUTF8(inURI), inExpSeconds);
|
||||
nsCAutoString pageURLString([inPageURL UTF8String]);
|
||||
nsCAutoString iconURLString;
|
||||
if (NS_SUCCEEDED(mIconsCacheHelper->GetFaviconLocationForPageURI(pageURLString, iconURLString)))
|
||||
{
|
||||
faviconURL = [NSString stringWith_nsACString:iconURLString];
|
||||
#ifdef VERBOSE_SITE_ICON_LOADING
|
||||
NSLog(@"found linked icon url %@ for page %@", faviconURL, inPageURL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (!faviconURL)
|
||||
faviconURL = [SiteIconProvider faviconLocationStringFromURI:inPageURL];
|
||||
|
||||
return faviconURL;
|
||||
}
|
||||
|
||||
- (void)addToMissedIconsCache:(NSString*)inURI withExpirationSeconds:(unsigned int)inExpSeconds
|
||||
{
|
||||
if (mIconsCacheHelper)
|
||||
{
|
||||
mIconsCacheHelper->SetFaviconForURIIsMissing(nsCString([inURI UTF8String]), inExpSeconds);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (BOOL)inMissedIconsCache:(const nsAString&)inURI
|
||||
- (BOOL)inMissedIconsCache:(NSString*)inURI
|
||||
{
|
||||
PRBool inCache = PR_FALSE;
|
||||
|
||||
if (mMissedIconsCacheHelper)
|
||||
mMissedIconsCacheHelper->ExistsInCache(NS_ConvertUCS2toUTF8(inURI), &inCache);
|
||||
if (mIconsCacheHelper)
|
||||
mIconsCacheHelper->FaviconForURIIsMissing(nsCString([inURI UTF8String]), &inCache);
|
||||
|
||||
return inCache;
|
||||
}
|
||||
|
||||
- (BOOL)loadFavoriteIcon:(id)sender forURI:(NSString *)inURI allowNetwork:(BOOL)inAllowNetwork
|
||||
{
|
||||
// look for a favicon
|
||||
nsAutoString uriString;
|
||||
[inURI assignTo_nsAString:uriString];
|
||||
|
||||
nsAutoString faviconURIString;
|
||||
MakeFaviconURIFromURI(uriString, faviconURIString);
|
||||
if (faviconURIString.Length() == 0)
|
||||
return NO;
|
||||
|
||||
NSString* faviconString = [NSString stringWith_nsAString:faviconURIString];
|
||||
|
||||
// is this uri already in the missing icons cache?
|
||||
if ([self inMissedIconsCache:faviconURIString])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
// preserve requesting URI for later notification
|
||||
[mRequestDict setObject:inURI forKey:faviconString];
|
||||
RemoteDataProvider* dataProvider = [RemoteDataProvider sharedRemoteDataProvider];
|
||||
return [dataProvider loadURI:faviconString forTarget:sender withListener:self withUserData:nil allowNetworking:inAllowNetwork];
|
||||
return [self fetchFavoriteIconForPage:inURI withIconLocation:nil allowNetwork:inAllowNetwork notifyingClient:sender];
|
||||
}
|
||||
|
||||
#define SITE_ICON_EXPIRATION_SECONDS (60 * 60 * 24 * 7) // 1 week
|
||||
@ -263,6 +381,10 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o
|
||||
// this is called on the main thread
|
||||
- (void)doneRemoteLoad:(NSString*)inURI forTarget:(id)target withUserData:(id)userData data:(NSData*)data status:(nsresult)status
|
||||
{
|
||||
#ifdef VERBOSE_SITE_ICON_LOADING
|
||||
NSLog(@"Site icon load %@ done, status 0x%08X", inURI, status);
|
||||
#endif
|
||||
|
||||
nsAutoString uriString;
|
||||
[inURI assignTo_nsAString:uriString];
|
||||
|
||||
@ -277,14 +399,14 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o
|
||||
NS_DURING
|
||||
faviconImage = [[[NSImage alloc] initWithData:data] autorelease];
|
||||
NS_HANDLER
|
||||
NSLog(@"Exception \"%@ making\" favicon image for %@", localException, inURI);
|
||||
NSLog(@"Exception \"%@\" making favicon image for %@", localException, inURI);
|
||||
faviconImage = nil;
|
||||
NS_ENDHANDLER
|
||||
|
||||
BOOL gotImageData = loadOK && (faviconImage != nil);
|
||||
if (NS_SUCCEEDED(status) && !gotImageData) // error status indicates that load was attempted from cache
|
||||
{
|
||||
[self addToMissedIconsCache:uriString withExpirationSeconds:SITE_ICON_EXPIRATION_SECONDS];
|
||||
[self addToMissedIconsCache:inURI withExpirationSeconds:SITE_ICON_EXPIRATION_SECONDS];
|
||||
}
|
||||
|
||||
if (gotImageData)
|
||||
@ -292,28 +414,139 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o
|
||||
[faviconImage setDataRetained:YES];
|
||||
[faviconImage setScalesWhenResized:YES];
|
||||
[faviconImage setSize:NSMakeSize(16, 16)];
|
||||
|
||||
// add the image to the cache
|
||||
[mIconDictionary setObject:faviconImage forKey:inURI];
|
||||
}
|
||||
|
||||
// figure out what URL triggered this favicon request
|
||||
NSMutableArray* requestList = [mRequestDict objectForKey:inURI];
|
||||
|
||||
// send out a notification for every object in the request list
|
||||
NSEnumerator* requestsEnum = [requestList objectEnumerator];
|
||||
NSDictionary* curRequest;
|
||||
while ((curRequest = [requestsEnum nextObject]))
|
||||
{
|
||||
NSString* requestURL = [curRequest objectForKey:@"request_uri"];
|
||||
if (!requestURL)
|
||||
requestURL = [NSString string];
|
||||
id requestor = [curRequest objectForKey:@"icon_requestor"];
|
||||
|
||||
// if we got an image, cache the link url, if any
|
||||
NSString* linkURL = [curRequest objectForKey:@"icon_uri"];
|
||||
if (gotImageData && ![linkURL isEqual:[NSNull null]] && mIconsCacheHelper)
|
||||
{
|
||||
nsCAutoString pageURI([requestURL UTF8String]);
|
||||
nsCAutoString iconURI([linkURL UTF8String]);
|
||||
|
||||
#ifdef VERBOSE_SITE_ICON_LOADING
|
||||
NSLog(@"saving linked icon url %@ for page %@", linkURL, requestURL);
|
||||
#endif
|
||||
mIconsCacheHelper->SaveFaviconLocationForPageURI(pageURI, iconURI, SITE_ICON_EXPIRATION_SECONDS);
|
||||
}
|
||||
|
||||
// we always send out the notification, so that clients know
|
||||
// about failed requests
|
||||
NSDictionary* notificationData = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
inURI, SiteIconLoadURIKey,
|
||||
faviconImage, SiteIconLoadImageKey, // may be nil
|
||||
requestURL, SiteIconLoadUserDataKey,
|
||||
nil];
|
||||
NSNotification *note = [NSNotification notificationWithName: SiteIconLoadNotificationName object:requestor userInfo:notificationData];
|
||||
[[NSNotificationQueue defaultQueue] enqueueNotification:note postingStyle:NSPostWhenIdle];
|
||||
}
|
||||
// figure out what URL triggered this favicon request
|
||||
|
||||
NSString *requestURL = [mRequestDict objectForKey:inURI];
|
||||
if (!requestURL)
|
||||
requestURL = [NSString string];
|
||||
|
||||
// we always send out the notification, so that clients know
|
||||
// about failed requests
|
||||
NSDictionary* notificationData = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
inURI, SiteIconLoadURIKey,
|
||||
faviconImage, SiteIconLoadImageKey, // may be nil
|
||||
requestURL, SiteIconLoadUserDataKey,
|
||||
nil];
|
||||
NSNotification *note = [NSNotification notificationWithName: SiteIconLoadNotificationName object:target userInfo:notificationData];
|
||||
[[NSNotificationQueue defaultQueue] enqueueNotification: note postingStyle:NSPostWhenIdle];
|
||||
// cleanup our key holder
|
||||
[mRequestDict removeObjectForKey:inURI];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (NSImage*)favoriteIconForPage:(NSString*)inPageURI
|
||||
{
|
||||
if ([inPageURI length] == 0)
|
||||
return nil;
|
||||
|
||||
// map uri to image location uri
|
||||
NSString* iconURL = [self favoriteIconURLFromPageURL:inPageURI];
|
||||
NSImage* siteIcon = [mIconDictionary objectForKey:iconURL];
|
||||
#ifdef VERBOSE_SITE_ICON_LOADING
|
||||
NSLog(@"got icon %@ for url %@", siteIcon, iconURL);
|
||||
#endif
|
||||
return siteIcon;
|
||||
}
|
||||
|
||||
- (BOOL)fetchFavoriteIconForPage:(NSString*)inPageURI
|
||||
withIconLocation:(NSString*)inIconURI
|
||||
allowNetwork:(BOOL)inAllowNetwork
|
||||
notifyingClient:(id)inClient
|
||||
{
|
||||
|
||||
NSString* iconTargetURL = nil;
|
||||
|
||||
if (inIconURI)
|
||||
{
|
||||
// XXX clear any old cached uri for this page here?
|
||||
|
||||
// we should deal with the case of a page with a link element asking
|
||||
// for the site icon, then a subsequent request with a nil inIconURI.
|
||||
// however, we normally try to load the "default" favicon.ico before
|
||||
// we see a link element, so the right thing happens.
|
||||
iconTargetURL = inIconURI;
|
||||
}
|
||||
else
|
||||
{
|
||||
// look in the cache, and if it's not found, use the default location
|
||||
iconTargetURL = [self favoriteIconURLFromPageURL:inPageURI];
|
||||
}
|
||||
|
||||
if ([iconTargetURL length] == 0)
|
||||
return NO;
|
||||
|
||||
// is this uri already in the missing icons cache?
|
||||
if ([self inMissedIconsCache:iconTargetURL])
|
||||
{
|
||||
#ifdef VERBOSE_SITE_ICON_LOADING
|
||||
NSLog(@"Site icon %@ known missing", iconTargetURL);
|
||||
#endif
|
||||
return NO;
|
||||
}
|
||||
|
||||
id iconLocationString = inIconURI;
|
||||
if (!iconLocationString)
|
||||
iconLocationString = [NSNull null];
|
||||
|
||||
// preserve requesting URI for later notification
|
||||
NSDictionary* loadInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
inClient, @"icon_requestor",
|
||||
inPageURI, @"request_uri",
|
||||
iconLocationString, @"icon_uri",
|
||||
nil];
|
||||
NSMutableArray* existingRequests = [mRequestDict objectForKey:iconTargetURL];
|
||||
if (existingRequests)
|
||||
{
|
||||
#ifdef VERBOSE_SITE_ICON_LOADING
|
||||
NSLog(@"Site icon request %@ added to load list", iconTargetURL);
|
||||
#endif
|
||||
[existingRequests addObject:loadInfo];
|
||||
return YES;
|
||||
}
|
||||
|
||||
existingRequests = [NSMutableArray arrayWithObject:loadInfo];
|
||||
[mRequestDict setObject:existingRequests forKey:iconTargetURL];
|
||||
|
||||
RemoteDataProvider* dataProvider = [RemoteDataProvider sharedRemoteDataProvider];
|
||||
BOOL loadDispatched = [dataProvider loadURI:iconTargetURL forTarget:inClient withListener:self withUserData:nil allowNetworking:inAllowNetwork];
|
||||
|
||||
#ifdef VERBOSE_SITE_ICON_LOADING
|
||||
NSLog(@"Site icon load %@ dispatched %d", iconTargetURL, loadDispatched);
|
||||
#endif
|
||||
|
||||
return loadDispatched;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
+ (SiteIconProvider*)sharedFavoriteIconProvider
|
||||
{
|
||||
static SiteIconProvider* sIconProvider = nil;
|
||||
|
@ -83,6 +83,11 @@ public:
|
||||
|
||||
void SetContainer(id <CHBrowserContainer> aContainer);
|
||||
|
||||
protected:
|
||||
|
||||
nsresult HandleBlockedPopupEvent(nsIDOMEvent* inEvent);
|
||||
nsresult HandleLinkAddedEvent(nsIDOMEvent* inEvent);
|
||||
|
||||
private:
|
||||
CHBrowserView* mView; // WEAK - it owns us
|
||||
NSMutableArray* mListeners;
|
||||
|
@ -43,7 +43,13 @@
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsIWebProgress.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOM3Document.h"
|
||||
#include "nsIDOMDocumentView.h"
|
||||
#include "nsIDOMAbstractView.h"
|
||||
#include "nsIDOMEventTarget.h"
|
||||
#include "nsIDOMPopupBlockedEvent.h"
|
||||
|
||||
// XPCOM and String includes
|
||||
@ -53,10 +59,11 @@
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsNetError.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
#import "CHBrowserView.h"
|
||||
|
||||
#include "CHBrowserListener.h"
|
||||
#import "CHBrowserListener.h"
|
||||
|
||||
|
||||
// informal protocol of methods that our embedding window might support
|
||||
@ -663,10 +670,28 @@ CHBrowserListener::SetContainer(id <CHBrowserContainer> aContainer)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CHBrowserListener::HandleEvent(nsIDOMEvent *event)
|
||||
CHBrowserListener::HandleEvent(nsIDOMEvent* inEvent)
|
||||
{
|
||||
nsCOMPtr<nsIDOMPopupBlockedEvent> blockEvent = do_QueryInterface(event);
|
||||
if ( blockEvent ) {
|
||||
NS_ENSURE_ARG(inEvent);
|
||||
|
||||
nsAutoString eventType;
|
||||
inEvent->GetType(eventType);
|
||||
|
||||
if (eventType.Equals(NS_LITERAL_STRING("DOMPopupBlocked")))
|
||||
return HandleBlockedPopupEvent(inEvent);
|
||||
|
||||
if (eventType.Equals(NS_LITERAL_STRING("DOMLinkAdded")))
|
||||
return HandleLinkAddedEvent(inEvent);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
CHBrowserListener::HandleBlockedPopupEvent(nsIDOMEvent* inEvent)
|
||||
{
|
||||
nsCOMPtr<nsIDOMPopupBlockedEvent> blockEvent = do_QueryInterface(inEvent);
|
||||
if (blockEvent) {
|
||||
nsCOMPtr<nsIURI> blockedURI, blockedSite;
|
||||
blockEvent->GetPopupWindowURI(getter_AddRefs(blockedURI));
|
||||
blockEvent->GetRequestingWindowURI(getter_AddRefs(blockedSite));
|
||||
@ -675,3 +700,77 @@ CHBrowserListener::HandleEvent(nsIDOMEvent *event)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CHBrowserListener::HandleLinkAddedEvent(nsIDOMEvent* inEvent)
|
||||
{
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
inEvent->GetTarget(getter_AddRefs(target));
|
||||
nsCOMPtr<nsIDOMElement> linkElement = do_QueryInterface(target);
|
||||
if (!linkElement)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsAutoString relAttribute;
|
||||
linkElement->GetAttribute(NS_LITERAL_STRING("rel"), relAttribute);
|
||||
|
||||
if (!relAttribute.EqualsIgnoreCase("shortcut icon") && !relAttribute.EqualsIgnoreCase("icon"))
|
||||
return NS_OK;
|
||||
|
||||
// make sure the load is for the main window
|
||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||
linkElement->GetOwnerDocument (getter_AddRefs(domDoc));
|
||||
|
||||
nsCOMPtr<nsIDOMDocumentView> docView(do_QueryInterface(domDoc));
|
||||
NS_ENSURE_TRUE(docView, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIDOMAbstractView> abstractView;
|
||||
docView->GetDefaultView(getter_AddRefs(abstractView));
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> domWin(do_QueryInterface(abstractView));
|
||||
NS_ENSURE_TRUE(domWin, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> topDomWin;
|
||||
domWin->GetTop(getter_AddRefs(topDomWin));
|
||||
|
||||
nsCOMPtr<nsISupports> domWinAsISupports(do_QueryInterface(domWin));
|
||||
nsCOMPtr<nsISupports> topDomWinAsISupports(do_QueryInterface(topDomWin));
|
||||
// prevent subframes from setting the favicon
|
||||
if (domWinAsISupports != topDomWinAsISupports)
|
||||
return NS_OK;
|
||||
|
||||
// now get the uri of the icon
|
||||
nsAutoString iconHref;
|
||||
linkElement->GetAttribute(NS_LITERAL_STRING("href"), iconHref);
|
||||
if (iconHref.IsEmpty())
|
||||
return NS_OK;
|
||||
|
||||
// get the document uri
|
||||
nsCOMPtr<nsIDOM3Document> doc = do_QueryInterface(domDoc);
|
||||
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
||||
|
||||
nsAutoString docURISpec;
|
||||
nsresult rv = doc->GetDocumentURI(docURISpec);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIURI> documentURI;
|
||||
rv = NS_NewURI(getter_AddRefs(documentURI), docURISpec);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIURI> iconURI;
|
||||
rv = NS_NewURI(getter_AddRefs(iconURI), NS_ConvertUCS2toUTF8(iconHref), nsnull, documentURI);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
|
||||
|
||||
// only accept http and https icons (should we allow https, even?)
|
||||
PRBool isHTTP = PR_FALSE, isHTTPS = PR_FALSE;
|
||||
iconURI->SchemeIs("http", &isHTTP);
|
||||
iconURI->SchemeIs("https", &isHTTPS);
|
||||
if (!isHTTP && !isHTTPS)
|
||||
return NS_OK;
|
||||
|
||||
nsCAutoString iconFullURI;
|
||||
iconURI->GetSpec(iconFullURI);
|
||||
NSString* iconSpec = [NSString stringWith_nsACString:iconFullURI];
|
||||
|
||||
[mContainer onFoundShortcutIcon:iconSpec];
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -80,6 +80,9 @@ class nsISupports;
|
||||
- (void)onHideTooltip;
|
||||
// Called when a popup is blocked
|
||||
- (void)onPopupBlocked:(nsIURI*)inURIBlocked fromSite:(nsIURI*)inSite;
|
||||
// Called when a "shortcut icon" link element is noticed
|
||||
- (void)onFoundShortcutIcon:(NSString*)inIconURI;
|
||||
|
||||
@end
|
||||
|
||||
typedef enum {
|
||||
|
@ -180,11 +180,19 @@ const char kDirServiceContractID[] = "@mozilla.org/file/directory_service;1";
|
||||
if ( rec )
|
||||
rec->AddEventListenerByIID(clickListener, NS_GET_IID(nsIDOMMouseListener));
|
||||
|
||||
// register the CHBrowserListener as an event listener for popup-blocking events
|
||||
// register the CHBrowserListener as an event listener for popup-blocking events,
|
||||
// and link-added events.
|
||||
nsCOMPtr<nsIDOMEventTarget> eventTarget = do_QueryInterface(rec);
|
||||
if ( eventTarget )
|
||||
if (eventTarget)
|
||||
{
|
||||
rv = eventTarget->AddEventListener(NS_LITERAL_STRING("DOMPopupBlocked"),
|
||||
NS_STATIC_CAST(nsIDOMEventListener*, _listener), PR_FALSE);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "AddEventListener failed");
|
||||
|
||||
rv = eventTarget->AddEventListener(NS_LITERAL_STRING("DOMLinkAdded"),
|
||||
NS_STATIC_CAST(nsIDOMEventListener*, _listener), PR_FALSE);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "AddEventListener failed");
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -58,10 +58,14 @@ static OSStatus MenuEventHandler(EventHandlerCallRef inHandlerCallRef, EventRef
|
||||
OSStatus err = GetEventParameter(inEvent, kEventParamDirectObject, typeMenuRef, NULL, sizeof(MenuRef), NULL, &theCarbonMenu);
|
||||
if (err == noErr)
|
||||
{
|
||||
// we can't map from MenuRef to NSMenu, so we have to let receivers of the notification
|
||||
// do the test.
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:NSMenuWillDisplayNotification
|
||||
object:[NSValue valueWithPointer:theCarbonMenu]];
|
||||
NS_DURING
|
||||
// we can't map from MenuRef to NSMenu, so we have to let receivers of the notification
|
||||
// do the test.
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:NSMenuWillDisplayNotification
|
||||
object:[NSValue valueWithPointer:theCarbonMenu]];
|
||||
NS_HANDLER
|
||||
NSLog(@"Caught exception %@", localException);
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -65,6 +65,7 @@ extern NSString* const kNotificationHistoryDataSourceChangedUserInfoChangedItemO
|
||||
nsIBrowserHistory* mGlobalHistory; // owned (would be an nsCOMPtr)
|
||||
nsHistoryObserver* mHistoryObserver; // owned
|
||||
|
||||
BOOL mShowSiteIcons;
|
||||
NSString* mCurrentViewIdentifier;
|
||||
|
||||
NSString* mSortColumn;
|
||||
@ -93,6 +94,8 @@ extern NSString* const kNotificationHistoryDataSourceChangedUserInfoChangedItemO
|
||||
- (void)loadLazily;
|
||||
- (HistoryItem*)rootItem;
|
||||
|
||||
- (BOOL)showSiteIcons;
|
||||
|
||||
// ideally sorting would be on the view, not the data source, but this keeps thing simpler
|
||||
- (void)setSortColumnIdentifier:(NSString*)sortColumnIdentifier;
|
||||
- (NSString*)sortColumnIdentifier;
|
||||
|
@ -47,6 +47,7 @@
|
||||
#import "ExtendedOutlineView.h"
|
||||
#import "PreferenceManager.h"
|
||||
#import "HistoryItem.h"
|
||||
#import "SiteIconProvider.h"
|
||||
|
||||
#import "BookmarkViewController.h" // only for +greyStringWithItemCount
|
||||
|
||||
@ -92,13 +93,14 @@ static int HistoryItemSort(id firstItem, id secondItem, void* context)
|
||||
// base class for a 'builder' object. This one just builds a flat list
|
||||
@interface HistoryTreeBuilder : NSObject
|
||||
{
|
||||
HistoryCategoryItem* mRootItem;
|
||||
HistoryDataSource* mDataSource; // not retained (it owns us)
|
||||
HistoryCategoryItem* mRootItem; // retained
|
||||
SEL mSortSelector;
|
||||
BOOL mSortDescending;
|
||||
}
|
||||
|
||||
// sets up the tree and sorts it
|
||||
- (id)initWithItems:(NSArray*)items sortSelector:(SEL)sortSelector descending:(BOOL)descending;
|
||||
- (id)initWithDataSource:(HistoryDataSource*)inDataSource items:(NSArray*)items sortSelector:(SEL)sortSelector descending:(BOOL)descending;
|
||||
|
||||
- (HistoryItem*)rootItem;
|
||||
- (HistoryItem*)addItem:(HistorySiteItem*)item;
|
||||
@ -159,6 +161,7 @@ static int HistoryItemSort(id firstItem, id secondItem, void* context)
|
||||
- (HistorySiteItem*)itemWithIdentifier:(NSString*)identifier;
|
||||
- (NSString*)relativeDataStringForDate:(NSDate*)date;
|
||||
|
||||
- (void)siteIconLoaded:(NSNotification*)inNotification;
|
||||
- (void)checkForNewDay;
|
||||
|
||||
@end
|
||||
@ -200,11 +203,12 @@ static int HistoryItemSort(id firstItem, id secondItem, void* context)
|
||||
|
||||
@implementation HistoryTreeBuilder
|
||||
|
||||
- (id)initWithItems:(NSArray*)items sortSelector:(SEL)sortSelector descending:(BOOL)descending
|
||||
- (id)initWithDataSource:(HistoryDataSource*)inDataSource items:(NSArray*)items sortSelector:(SEL)sortSelector descending:(BOOL)descending
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
mSortSelector = sortSelector;
|
||||
mDataSource = inDataSource; // not retained
|
||||
mSortSelector = sortSelector;
|
||||
mSortDescending = descending;
|
||||
|
||||
[self buildTreeWithItems:items];
|
||||
@ -239,7 +243,7 @@ static int HistoryItemSort(id firstItem, id secondItem, void* context)
|
||||
|
||||
- (void)buildTreeWithItems:(NSArray*)items
|
||||
{
|
||||
mRootItem = [[HistoryCategoryItem alloc] initWithTitle:@"" childCapacity:[items count]];
|
||||
mRootItem = [[HistoryCategoryItem alloc] initWithDataSource:mDataSource title:@"" childCapacity:[items count]];
|
||||
|
||||
[mRootItem addChildren:items];
|
||||
[self resortFromItem:mRootItem];
|
||||
@ -297,7 +301,7 @@ static int HistoryItemSort(id firstItem, id secondItem, void* context)
|
||||
if ([itemHostname isEqualToString:@"local_file"])
|
||||
itemTitle = NSLocalizedString(@"LocalFilesCategoryTitle", @"");
|
||||
|
||||
hostCategory = [[HistorySiteCategoryItem alloc] initWithSite:itemHostname title:itemTitle childCapacity:10];
|
||||
hostCategory = [[HistorySiteCategoryItem alloc] initWithDataSource:mDataSource site:itemHostname title:itemTitle childCapacity:10];
|
||||
[mSiteDictionary setObject:hostCategory forKey:itemHostname];
|
||||
[mRootItem addChild:hostCategory];
|
||||
[hostCategory release];
|
||||
@ -349,7 +353,7 @@ static int HistoryItemSort(id firstItem, id secondItem, void* context)
|
||||
[mSiteDictionary removeAllObjects];
|
||||
|
||||
[mRootItem release];
|
||||
mRootItem = [[HistoryCategoryItem alloc] initWithTitle:@"" childCapacity:100];
|
||||
mRootItem = [[HistoryCategoryItem alloc] initWithDataSource:mDataSource title:@"" childCapacity:100];
|
||||
|
||||
NSEnumerator* itemsEnum = [items objectEnumerator];
|
||||
HistorySiteItem* item;
|
||||
@ -404,7 +408,7 @@ static int HistoryItemSort(id firstItem, id secondItem, void* context)
|
||||
minute:0
|
||||
second:0
|
||||
timeZone:[nowDate timeZone]];
|
||||
HistoryCategoryItem* todayItem = [[HistoryDateCategoryItem alloc] initWithStartDate:lastMidnight ageInDays:0 title:NSLocalizedString(@"Today", @"") childCapacity:10];
|
||||
HistoryCategoryItem* todayItem = [[HistoryDateCategoryItem alloc] initWithDataSource:mDataSource startDate:lastMidnight ageInDays:0 title:NSLocalizedString(@"Today", @"") childCapacity:10];
|
||||
[mDateCategories addObject:todayItem];
|
||||
[todayItem release];
|
||||
|
||||
@ -414,7 +418,7 @@ static int HistoryItemSort(id firstItem, id secondItem, void* context)
|
||||
hours:0
|
||||
minutes:0
|
||||
seconds:0];
|
||||
HistoryCategoryItem* yesterdayItem = [[HistoryDateCategoryItem alloc] initWithStartDate:startYesterday ageInDays:1 title:NSLocalizedString(@"Yesterday", @"") childCapacity:10];
|
||||
HistoryCategoryItem* yesterdayItem = [[HistoryDateCategoryItem alloc] initWithDataSource:mDataSource startDate:startYesterday ageInDays:1 title:NSLocalizedString(@"Yesterday", @"") childCapacity:10];
|
||||
[mDateCategories addObject:yesterdayItem];
|
||||
[yesterdayItem release];
|
||||
|
||||
@ -430,14 +434,14 @@ static int HistoryItemSort(id firstItem, id secondItem, void* context)
|
||||
minutes:0
|
||||
seconds:0];
|
||||
|
||||
HistoryCategoryItem* dayItem = [[HistoryDateCategoryItem alloc] initWithStartDate:curDayStart ageInDays:(i + 2) title:[curDayStart descriptionWithCalendarFormat:@"%A %B %d"] childCapacity:10];
|
||||
HistoryCategoryItem* dayItem = [[HistoryDateCategoryItem alloc] initWithDataSource:mDataSource startDate:curDayStart ageInDays:(i + 2) title:[curDayStart descriptionWithCalendarFormat:@"%A %B %d"] childCapacity:10];
|
||||
[mDateCategories addObject:dayItem];
|
||||
[dayItem release];
|
||||
}
|
||||
|
||||
// do "older"
|
||||
NSDate* oldDate = [NSDate distantPast];
|
||||
HistoryCategoryItem* olderItem = [[HistoryDateCategoryItem alloc] initWithStartDate:oldDate ageInDays:-1 title:NSLocalizedString(@"HistoryMoreThanAWeek", @"") childCapacity:100];
|
||||
HistoryCategoryItem* olderItem = [[HistoryDateCategoryItem alloc] initWithDataSource:mDataSource startDate:oldDate ageInDays:-1 title:NSLocalizedString(@"HistoryMoreThanAWeek", @"") childCapacity:100];
|
||||
[mDateCategories addObject:olderItem];
|
||||
[olderItem release];
|
||||
}
|
||||
@ -485,7 +489,7 @@ static int HistoryItemSort(id firstItem, id secondItem, void* context)
|
||||
[dateCategory addChild:item];
|
||||
}
|
||||
|
||||
mRootItem = [[HistoryCategoryItem alloc] initWithTitle:@"" childCapacity:[mDateCategories count]];
|
||||
mRootItem = [[HistoryCategoryItem alloc] initWithDataSource:mDataSource title:@"" childCapacity:[mDateCategories count]];
|
||||
[mRootItem addChildren:mDateCategories];
|
||||
|
||||
[self resortFromItem:mRootItem];
|
||||
@ -548,7 +552,7 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
item = [[HistorySiteItem alloc] initWith_nsIHistoryItem:inHistoryItem];
|
||||
item = [[HistorySiteItem alloc] initWithDataSource:mDataSource historyItem:inHistoryItem];
|
||||
[mDataSource itemAdded:item];
|
||||
[item release];
|
||||
}
|
||||
@ -650,6 +654,14 @@ NS_IMPL_ISUPPORTS1(nsHistoryObserver, nsIHistoryObserver);
|
||||
selector:@selector(refreshTimerFired:)
|
||||
userInfo:nil
|
||||
repeats:YES] retain];
|
||||
|
||||
// register for site icon loads
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(siteIconLoaded:)
|
||||
name:SiteIconLoadNotificationName
|
||||
object:nil];
|
||||
|
||||
mShowSiteIcons = [[PreferenceManager sharedInstance] getBooleanPref:"browser.chrome.favicons" withSuccess:NULL];
|
||||
}
|
||||
|
||||
return self;
|
||||
@ -788,7 +800,7 @@ NS_IMPL_ISUPPORTS1(nsHistoryObserver, nsIHistoryObserver);
|
||||
nsCOMPtr<nsIHistoryItem> thisItem = do_QueryInterface(thisEntry);
|
||||
if (thisItem)
|
||||
{
|
||||
HistorySiteItem* item = [[HistorySiteItem alloc] initWith_nsIHistoryItem:thisItem];
|
||||
HistorySiteItem* item = [[HistorySiteItem alloc] initWithDataSource:self historyItem:thisItem];
|
||||
[mHistoryItems addObject:item];
|
||||
[mHistoryItemsDictionary setObject:item forKey:[item identifier]];
|
||||
[item release];
|
||||
@ -803,6 +815,11 @@ NS_IMPL_ISUPPORTS1(nsHistoryObserver, nsIHistoryObserver);
|
||||
return [mTreeBuilder rootItem];
|
||||
}
|
||||
|
||||
- (BOOL)showSiteIcons
|
||||
{
|
||||
return mShowSiteIcons;
|
||||
}
|
||||
|
||||
- (void)notifyChanged:(HistoryItem*)changeRoot itemOnly:(BOOL)itemOnly
|
||||
{
|
||||
// if we are displaying search results, make sure that updates
|
||||
@ -913,6 +930,22 @@ NS_IMPL_ISUPPORTS1(nsHistoryObserver, nsIHistoryObserver);
|
||||
return [calendarDate relativeDateDescription];
|
||||
}
|
||||
|
||||
- (void)siteIconLoaded:(NSNotification*)inNotification
|
||||
{
|
||||
HistoryItem* theItem = [inNotification object];
|
||||
// if it's not a site item, or it doesn't belong to this data source, ignore it
|
||||
// (we instantiate multiple data sources)
|
||||
if (![theItem isKindOfClass:[HistorySiteItem class]] || [theItem dataSource] != self)
|
||||
return;
|
||||
|
||||
NSImage* iconImage = [[inNotification userInfo] objectForKey:SiteIconLoadImageKey];
|
||||
if (iconImage)
|
||||
{
|
||||
[theItem setSiteIcon:iconImage];
|
||||
[self notifyChanged:theItem itemOnly:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)checkForNewDay
|
||||
{
|
||||
int curDayOfCommonEra = [[NSCalendarDate calendarDate] dayOfCommonEra];
|
||||
|
@ -39,17 +39,23 @@
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
class nsIHistoryItem;
|
||||
@class HistoryDataSource;
|
||||
|
||||
// HistoryItem is the base class for every object in the history outliner
|
||||
|
||||
@interface HistoryItem : NSObject
|
||||
{
|
||||
HistoryItem* mParentItem; // not retained
|
||||
HistoryItem* mParentItem; // our parent item (not retained)
|
||||
HistoryDataSource* mDataSource; // the data source that owns us (not retained)
|
||||
}
|
||||
|
||||
- (id)initWithDataSource:(HistoryDataSource*)inDataSource;
|
||||
- (HistoryDataSource*)dataSource;
|
||||
|
||||
- (NSString*)title;
|
||||
- (BOOL)isSiteItem;
|
||||
- (NSImage*)icon;
|
||||
- (NSImage*)iconAllowingLoad:(BOOL)inAllowLoad;
|
||||
|
||||
- (NSString*)url;
|
||||
- (NSDate*)firstVisit;
|
||||
@ -84,7 +90,7 @@ class nsIHistoryItem;
|
||||
NSMutableArray* mChildren; // array of HistoryItems (may be heterogeneous)
|
||||
}
|
||||
|
||||
- (id)initWithTitle:(NSString*)title childCapacity:(int)capacity;
|
||||
- (id)initWithDataSource:(HistoryDataSource*)inDataSource title:(NSString*)title childCapacity:(int)capacity;
|
||||
- (NSString*)title;
|
||||
- (NSString*)identifier; // return UUID for this folder
|
||||
|
||||
@ -101,7 +107,7 @@ class nsIHistoryItem;
|
||||
NSString* mSite; // not user-visible; used for state tracking
|
||||
}
|
||||
|
||||
- (id)initWithSite:(NSString*)site title:(NSString*)title childCapacity:(int)capacity;
|
||||
- (id)initWithDataSource:(HistoryDataSource*)inDataSource site:(NSString*)site title:(NSString*)title childCapacity:(int)capacity;
|
||||
- (NSString*)site;
|
||||
|
||||
@end
|
||||
@ -113,7 +119,7 @@ class nsIHistoryItem;
|
||||
int mAgeInDays; // -1 is used for "distant past"
|
||||
}
|
||||
|
||||
- (id)initWithStartDate:(NSDate*)startDate ageInDays:(int)days title:(NSString*)title childCapacity:(int)capacity;
|
||||
- (id)initWithDataSource:(HistoryDataSource*)inDataSource startDate:(NSDate*)startDate ageInDays:(int)days title:(NSString*)title childCapacity:(int)capacity;
|
||||
- (NSDate*)startDate;
|
||||
- (BOOL)isTodayCategory;
|
||||
|
||||
@ -128,12 +134,17 @@ class nsIHistoryItem;
|
||||
NSString* mHostname;
|
||||
NSDate* mFirstVisitDate;
|
||||
NSDate* mLastVisitDate;
|
||||
|
||||
NSImage* mSiteIcon;
|
||||
BOOL mAttemptedIconLoad;
|
||||
}
|
||||
|
||||
- (id)initWith_nsIHistoryItem:(nsIHistoryItem*)inItem;
|
||||
- (id)initWithDataSource:(HistoryDataSource*)inDataSource historyItem:(nsIHistoryItem*)inItem;
|
||||
// return YES if anything changed
|
||||
- (BOOL)updateWith_nsIHistoryItem:(nsIHistoryItem*)inItem;
|
||||
|
||||
- (BOOL)matchesString:(NSString*)searchString inFieldWithTag:(int)tag;
|
||||
|
||||
- (void)setSiteIcon:(NSImage*)inImage;
|
||||
|
||||
@end
|
||||
|
@ -40,6 +40,9 @@
|
||||
#import "NSDate+Utils.h"
|
||||
|
||||
#import "HistoryItem.h"
|
||||
#import "HistoryDataSource.h"
|
||||
|
||||
#import "SiteIconProvider.h"
|
||||
|
||||
#import "nsString.h"
|
||||
#import "nsIHistoryItems.h"
|
||||
@ -56,10 +59,11 @@ enum
|
||||
|
||||
@implementation HistoryItem
|
||||
|
||||
- (id)init
|
||||
- (id)initWithDataSource:(HistoryDataSource*)inDataSource
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
mDataSource = inDataSource; // not retained
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -69,6 +73,11 @@ enum
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (HistoryDataSource*)dataSource
|
||||
{
|
||||
return mDataSource;
|
||||
}
|
||||
|
||||
- (NSString*)title
|
||||
{
|
||||
return @""; // subclasses override
|
||||
@ -84,6 +93,11 @@ enum
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSImage*)iconAllowingLoad:(BOOL)inAllowLoad
|
||||
{
|
||||
return [self icon];
|
||||
}
|
||||
|
||||
- (NSString*)url
|
||||
{
|
||||
return @"";
|
||||
@ -173,9 +187,9 @@ enum
|
||||
|
||||
@implementation HistoryCategoryItem
|
||||
|
||||
- (id)initWithTitle:(NSString*)title childCapacity:(int)capacity
|
||||
- (id)initWithDataSource:(HistoryDataSource*)inDataSource title:(NSString*)title childCapacity:(int)capacity
|
||||
{
|
||||
if ((self = [super init]))
|
||||
if ((self = [super initWithDataSource:inDataSource]))
|
||||
{
|
||||
mTitle = [title retain];
|
||||
mChildren = [[NSMutableArray alloc] initWithCapacity:capacity];
|
||||
@ -337,9 +351,9 @@ enum
|
||||
|
||||
@implementation HistorySiteCategoryItem
|
||||
|
||||
- (id)initWithSite:(NSString*)site title:(NSString*)title childCapacity:(int)capacity
|
||||
- (id)initWithDataSource:(HistoryDataSource*)inDataSource site:(NSString*)site title:(NSString*)title childCapacity:(int)capacity
|
||||
{
|
||||
if ((self = [super initWithTitle:title childCapacity:capacity]))
|
||||
if ((self = [super initWithDataSource:inDataSource title:title childCapacity:capacity]))
|
||||
{
|
||||
mSite = [site retain];
|
||||
}
|
||||
@ -373,9 +387,9 @@ enum
|
||||
|
||||
@implementation HistoryDateCategoryItem
|
||||
|
||||
- (id)initWithStartDate:(NSDate*)startDate ageInDays:(int)days title:(NSString*)title childCapacity:(int)capacity
|
||||
- (id)initWithDataSource:(HistoryDataSource*)inDataSource startDate:(NSDate*)startDate ageInDays:(int)days title:(NSString*)title childCapacity:(int)capacity
|
||||
{
|
||||
if ((self = [super initWithTitle:title childCapacity:capacity]))
|
||||
if ((self = [super initWithDataSource:inDataSource title:title childCapacity:capacity]))
|
||||
{
|
||||
mStartDate = [startDate retain];
|
||||
mAgeInDays = days;
|
||||
@ -456,9 +470,9 @@ enum
|
||||
|
||||
@implementation HistorySiteItem
|
||||
|
||||
- (id)initWith_nsIHistoryItem:(nsIHistoryItem*)inItem
|
||||
- (id)initWithDataSource:(HistoryDataSource*)inDataSource historyItem:(nsIHistoryItem*)inItem
|
||||
{
|
||||
if ((self = [super init]))
|
||||
if ((self = [super initWithDataSource:inDataSource]))
|
||||
{
|
||||
nsCString identifier;
|
||||
if (NS_SUCCEEDED(inItem->GetID(identifier)))
|
||||
@ -530,6 +544,8 @@ enum
|
||||
[mHostname release];
|
||||
[mFirstVisitDate release];
|
||||
[mLastVisitDate release];
|
||||
[mSiteIcon release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@ -570,8 +586,46 @@ enum
|
||||
|
||||
- (NSImage*)icon
|
||||
{
|
||||
// XXX todo: site icons
|
||||
return [NSImage imageNamed:@"smallbookmark"];
|
||||
return [self iconAllowingLoad:NO];
|
||||
}
|
||||
|
||||
- (NSImage*)iconAllowingLoad:(BOOL)inAllowLoad
|
||||
{
|
||||
if (mSiteIcon)
|
||||
return mSiteIcon;
|
||||
|
||||
if ([mDataSource showSiteIcons])
|
||||
{
|
||||
NSImage* siteIcon = [[SiteIconProvider sharedFavoriteIconProvider] favoriteIconForPage:[self url]];
|
||||
if (siteIcon)
|
||||
{
|
||||
[self setSiteIcon:siteIcon];
|
||||
return mSiteIcon;
|
||||
}
|
||||
}
|
||||
|
||||
// firing off site icon loads here interferes with history submenu display
|
||||
// (maybe a slew of Carbon or other events causes events to get lost?)
|
||||
if (inAllowLoad)
|
||||
{
|
||||
if (!mAttemptedIconLoad)
|
||||
{
|
||||
// fire off site icon load
|
||||
[[SiteIconProvider sharedFavoriteIconProvider] fetchFavoriteIconForPage:[self url]
|
||||
withIconLocation:nil
|
||||
allowNetwork:NO
|
||||
notifyingClient:self];
|
||||
mAttemptedIconLoad = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return [NSImage imageNamed:@"globe_ico"];
|
||||
}
|
||||
|
||||
- (void)setSiteIcon:(NSImage*)inImage
|
||||
{
|
||||
[mSiteIcon autorelease];
|
||||
mSiteIcon = [inImage retain];
|
||||
}
|
||||
|
||||
// ideally, we'd strip the protocol from the URL before comparing so that https:// doesn't
|
||||
|
@ -334,7 +334,7 @@ static NSString* const kExpandedHistoryStatesDefaultsKey = @"history_expand_stat
|
||||
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
|
||||
{
|
||||
if ([[tableColumn identifier] isEqualToString:@"title"])
|
||||
[cell setImage:[item icon]];
|
||||
[cell setImage:[item iconAllowingLoad:YES]];
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
Loading…
Reference in New Issue
Block a user