diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:52 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:52 -0800 |
commit | 8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (patch) | |
tree | 11425ea0b299d6fb89c6d3618a22d97d5bf68d0f /WebKit/mac/History/WebHistory.mm | |
parent | 648161bb0edfc3d43db63caed5cc5213bc6cb78f (diff) | |
download | external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.zip external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.gz external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'WebKit/mac/History/WebHistory.mm')
-rw-r--r-- | WebKit/mac/History/WebHistory.mm | 784 |
1 files changed, 784 insertions, 0 deletions
diff --git a/WebKit/mac/History/WebHistory.mm b/WebKit/mac/History/WebHistory.mm new file mode 100644 index 0000000..1eedd8e --- /dev/null +++ b/WebKit/mac/History/WebHistory.mm @@ -0,0 +1,784 @@ +/* + * Copyright (C) 2005, 2008 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 "WebHistoryInternal.h" + +#import "WebHistoryItemInternal.h" +#import "WebKitLogging.h" +#import "WebNSURLExtras.h" +#import "WebTypesInternal.h" +#import <Foundation/NSError.h> +#import <WebCore/Page.h> +#import <WebCore/PageGroup.h> +#import <wtf/Assertions.h> +#import <wtf/HashMap.h> +#import <wtf/RetainPtr.h> +#import <wtf/Vector.h> + +using namespace WebCore; + +typedef int64_t WebHistoryDateKey; +typedef HashMap<WebHistoryDateKey, RetainPtr<NSMutableArray> > DateToEntriesMap; + +NSString *WebHistoryItemsAddedNotification = @"WebHistoryItemsAddedNotification"; +NSString *WebHistoryItemsRemovedNotification = @"WebHistoryItemsRemovedNotification"; +NSString *WebHistoryAllItemsRemovedNotification = @"WebHistoryAllItemsRemovedNotification"; +NSString *WebHistoryLoadedNotification = @"WebHistoryLoadedNotification"; +NSString *WebHistoryItemsDiscardedWhileLoadingNotification = @"WebHistoryItemsDiscardedWhileLoadingNotification"; +NSString *WebHistorySavedNotification = @"WebHistorySavedNotification"; +NSString *WebHistoryItemsKey = @"WebHistoryItems"; + +static WebHistory *_sharedHistory = nil; + +NSString *FileVersionKey = @"WebHistoryFileVersion"; +NSString *DatesArrayKey = @"WebHistoryDates"; + +#define currentFileVersion 1 + +@interface WebHistoryPrivate : NSObject { +@private + NSMutableDictionary *_entriesByURL; + DateToEntriesMap* _entriesByDate; + NSMutableArray *_orderedLastVisitedDays; + BOOL itemLimitSet; + int itemLimit; + BOOL ageInDaysLimitSet; + int ageInDaysLimit; +} + +- (void)addItem:(WebHistoryItem *)entry; +- (void)addItems:(NSArray *)newEntries; +- (BOOL)removeItem:(WebHistoryItem *)entry; +- (BOOL)removeItems:(NSArray *)entries; +- (BOOL)removeAllItems; + +- (NSArray *)orderedLastVisitedDays; +- (NSArray *)orderedItemsLastVisitedOnDay:(NSCalendarDate *)calendarDate; +- (BOOL)containsURL:(NSURL *)URL; +- (WebHistoryItem *)itemForURL:(NSURL *)URL; +- (WebHistoryItem *)itemForURLString:(NSString *)URLString; + +- (BOOL)loadFromURL:(NSURL *)URL collectDiscardedItemsInto:(NSMutableArray *)discardedItems error:(NSError **)error; +- (BOOL)saveToURL:(NSURL *)URL error:(NSError **)error; + +- (NSCalendarDate *)ageLimitDate; + +- (void)setHistoryItemLimit:(int)limit; +- (int)historyItemLimit; +- (void)setHistoryAgeInDaysLimit:(int)limit; +- (int)historyAgeInDaysLimit; + +- (void)addVisitedLinksToPageGroup:(PageGroup&)group; + +@end + +@implementation WebHistoryPrivate + +#pragma mark OBJECT FRAMEWORK + ++ (void)initialize +{ + [[NSUserDefaults standardUserDefaults] registerDefaults: + [NSDictionary dictionaryWithObjectsAndKeys: + @"1000", @"WebKitHistoryItemLimit", + @"7", @"WebKitHistoryAgeInDaysLimit", + nil]]; +} + +- (id)init +{ + if (![super init]) + return nil; + + _entriesByURL = [[NSMutableDictionary alloc] init]; + _entriesByDate = new DateToEntriesMap; + + return self; +} + +- (void)dealloc +{ + [_entriesByURL release]; + [_orderedLastVisitedDays release]; + delete _entriesByDate; + [super dealloc]; +} + +- (void)finalize +{ + delete _entriesByDate; + [super finalize]; +} + +#pragma mark MODIFYING CONTENTS + +WebHistoryDateKey timeIntervalForBeginningOfDay(NSTimeInterval interval) +{ + CFTimeZoneRef timeZone = CFTimeZoneCopyDefault(); + CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(interval, timeZone); + date.hour = 0; + date.minute = 0; + date.second = 0; + NSTimeInterval result = CFGregorianDateGetAbsoluteTime(date, timeZone); + CFRelease(timeZone); + + // Converting from double to int64_t is safe here as NSDate's useful range + // is -2**48 .. 2**47 which will safely fit in an int64_t. + return (WebHistoryDateKey)result; +} + +// Returns whether the day is already in the list of days, +// and fills in *key with the key used to access its location +- (BOOL)findKey:(WebHistoryDateKey*)key forDay:(NSTimeInterval)date +{ + ASSERT_ARG(key, key != nil); + *key = timeIntervalForBeginningOfDay(date); + return _entriesByDate->contains(*key); +} + +- (void)insertItem:(WebHistoryItem *)entry forDateKey:(WebHistoryDateKey)dateKey +{ + ASSERT_ARG(entry, entry != nil); + ASSERT(_entriesByDate->contains(dateKey)); + + NSMutableArray *entriesForDate = _entriesByDate->get(dateKey).get(); + NSTimeInterval entryDate = [entry lastVisitedTimeInterval]; + + unsigned count = [entriesForDate count]; + + // The entries for each day are stored in a sorted array with the most recent entry first + // Check for the common cases of the entry being newer than all existing entries or the first entry of the day + if (!count || [[entriesForDate objectAtIndex:0] lastVisitedTimeInterval] < entryDate) { + [entriesForDate insertObject:entry atIndex:0]; + return; + } + // .. or older than all existing entries + if (count > 0 && [[entriesForDate objectAtIndex:count - 1] lastVisitedTimeInterval] >= entryDate) { + [entriesForDate insertObject:entry atIndex:count]; + return; + } + + unsigned low = 0; + unsigned high = count; + while (low < high) { + unsigned mid = low + (high - low) / 2; + if ([[entriesForDate objectAtIndex:mid] lastVisitedTimeInterval] >= entryDate) + low = mid + 1; + else + high = mid; + } + + // low is now the index of the first entry that is older than entryDate + [entriesForDate insertObject:entry atIndex:low]; +} + +- (BOOL)removeItemFromDateCaches:(WebHistoryItem *)entry +{ + WebHistoryDateKey dateKey; + BOOL foundDate = [self findKey:&dateKey forDay:[entry lastVisitedTimeInterval]]; + + if (!foundDate) + return NO; + + DateToEntriesMap::iterator it = _entriesByDate->find(dateKey); + NSMutableArray *entriesForDate = it->second.get(); + [entriesForDate removeObjectIdenticalTo:entry]; + + // remove this date entirely if there are no other entries on it + if ([entriesForDate count] == 0) { + _entriesByDate->remove(it); + // Clear _orderedLastVisitedDays so it will be regenerated when next requested. + [_orderedLastVisitedDays release]; + _orderedLastVisitedDays = nil; + } + + return YES; +} + +- (BOOL)removeItemForURLString:(NSString *)URLString +{ + WebHistoryItem *entry = [_entriesByURL objectForKey:URLString]; + if (!entry) + return NO; + + [_entriesByURL removeObjectForKey:URLString]; + +#if ASSERT_DISABLED + [self removeItemFromDateCaches:entry]; +#else + BOOL itemWasInDateCaches = [self removeItemFromDateCaches:entry]; + ASSERT(itemWasInDateCaches); +#endif + + if (![_entriesByURL count]) + PageGroup::removeAllVisitedLinks(); + + return YES; +} + +- (void)addItemToDateCaches:(WebHistoryItem *)entry +{ + WebHistoryDateKey dateKey; + if ([self findKey:&dateKey forDay:[entry lastVisitedTimeInterval]]) + // other entries already exist for this date + [self insertItem:entry forDateKey:dateKey]; + else { + // no other entries exist for this date + NSMutableArray *entries = [[NSMutableArray alloc] initWithObjects:&entry count:1]; + _entriesByDate->set(dateKey, entries); + [entries release]; + // Clear _orderedLastVisitedDays so it will be regenerated when next requested. + [_orderedLastVisitedDays release]; + _orderedLastVisitedDays = nil; + } +} + +- (void)addItem:(WebHistoryItem *)entry +{ + ASSERT_ARG(entry, entry); + ASSERT_ARG(entry, [entry lastVisitedTimeInterval] != 0); + + NSString *URLString = [entry URLString]; + + WebHistoryItem *oldEntry = [_entriesByURL objectForKey:URLString]; + if (oldEntry) { + // The last reference to oldEntry might be this dictionary, so we hold onto a reference + // until we're done with oldEntry. + [oldEntry retain]; + [self removeItemForURLString:URLString]; + + // If we already have an item with this URL, we need to merge info that drives the + // URL autocomplete heuristics from that item into the new one. + [entry _mergeAutoCompleteHints:oldEntry]; + [oldEntry release]; + } + + [self addItemToDateCaches:entry]; + [_entriesByURL setObject:entry forKey:URLString]; +} + +- (BOOL)removeItem:(WebHistoryItem *)entry +{ + NSString *URLString = [entry URLString]; + + // If this exact object isn't stored, then make no change. + // FIXME: Is this the right behavior if this entry isn't present, but another entry for the same URL is? + // Maybe need to change the API to make something like removeEntryForURLString public instead. + WebHistoryItem *matchingEntry = [_entriesByURL objectForKey:URLString]; + if (matchingEntry != entry) + return NO; + + [self removeItemForURLString:URLString]; + + return YES; +} + +- (BOOL)removeItems:(NSArray *)entries +{ + NSUInteger count = [entries count]; + if (!count) + return NO; + + for (NSUInteger index = 0; index < count; ++index) + [self removeItem:[entries objectAtIndex:index]]; + + return YES; +} + +- (BOOL)removeAllItems +{ + if (_entriesByDate->isEmpty()) + return NO; + + _entriesByDate->clear(); + [_entriesByURL removeAllObjects]; + + // Clear _orderedLastVisitedDays so it will be regenerated when next requested. + [_orderedLastVisitedDays release]; + _orderedLastVisitedDays = nil; + + PageGroup::removeAllVisitedLinks(); + + return YES; +} + +- (void)addItems:(NSArray *)newEntries +{ + // There is no guarantee that the incoming entries are in any particular + // order, but if this is called with a set of entries that were created by + // iterating through the results of orderedLastVisitedDays and orderedItemsLastVisitedOnDayy + // then they will be ordered chronologically from newest to oldest. We can make adding them + // faster (fewer compares) by inserting them from oldest to newest. + NSEnumerator *enumerator = [newEntries reverseObjectEnumerator]; + while (WebHistoryItem *entry = [enumerator nextObject]) + [self addItem:entry]; +} + +#pragma mark DATE-BASED RETRIEVAL + +- (NSArray *)orderedLastVisitedDays +{ + if (!_orderedLastVisitedDays) { + Vector<int> daysAsTimeIntervals; + daysAsTimeIntervals.reserveCapacity(_entriesByDate->size()); + DateToEntriesMap::const_iterator end = _entriesByDate->end(); + for (DateToEntriesMap::const_iterator it = _entriesByDate->begin(); it != end; ++it) + daysAsTimeIntervals.append(it->first); + + std::sort(daysAsTimeIntervals.begin(), daysAsTimeIntervals.end()); + size_t count = daysAsTimeIntervals.size(); + _orderedLastVisitedDays = [[NSMutableArray alloc] initWithCapacity:count]; + for (int i = count - 1; i >= 0; i--) { + NSTimeInterval interval = daysAsTimeIntervals[i]; + NSCalendarDate *date = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:interval]; + [_orderedLastVisitedDays addObject:date]; + [date release]; + } + } + return _orderedLastVisitedDays; +} + +- (NSArray *)orderedItemsLastVisitedOnDay:(NSCalendarDate *)date +{ + WebHistoryDateKey dateKey; + if (![self findKey:&dateKey forDay:[date timeIntervalSinceReferenceDate]]) + return nil; + return _entriesByDate->get(dateKey).get(); +} + +#pragma mark URL MATCHING + +- (WebHistoryItem *)itemForURLString:(NSString *)URLString +{ + return [_entriesByURL objectForKey:URLString]; +} + +- (BOOL)containsURL:(NSURL *)URL +{ + return [self itemForURLString:[URL _web_originalDataAsString]] != nil; +} + +- (WebHistoryItem *)itemForURL:(NSURL *)URL +{ + return [self itemForURLString:[URL _web_originalDataAsString]]; +} + +#pragma mark ARCHIVING/UNARCHIVING + +- (void)setHistoryAgeInDaysLimit:(int)limit +{ + ageInDaysLimitSet = YES; + ageInDaysLimit = limit; +} + +- (int)historyAgeInDaysLimit +{ + if (ageInDaysLimitSet) + return ageInDaysLimit; + return [[NSUserDefaults standardUserDefaults] integerForKey:@"WebKitHistoryAgeInDaysLimit"]; +} + +- (void)setHistoryItemLimit:(int)limit +{ + itemLimitSet = YES; + itemLimit = limit; +} + +- (int)historyItemLimit +{ + if (itemLimitSet) + return itemLimit; + return [[NSUserDefaults standardUserDefaults] integerForKey:@"WebKitHistoryItemLimit"]; +} + +// Return a date that marks the age limit for history entries saved to or +// loaded from disk. Any entry older than this item should be rejected. +- (NSCalendarDate *)ageLimitDate +{ + return [[NSCalendarDate calendarDate] dateByAddingYears:0 months:0 days:-[self historyAgeInDaysLimit] + hours:0 minutes:0 seconds:0]; +} + +// Return a flat array of WebHistoryItems. Ignores the date and item count limits; these are +// respected when loading instead of when saving, so that clients can learn of discarded items +// by listening to WebHistoryItemsDiscardedWhileLoadingNotification. +- (NSArray *)arrayRepresentation +{ + NSMutableArray *arrayRep = [NSMutableArray array]; + + Vector<int> dateKeys; + dateKeys.reserveCapacity(_entriesByDate->size()); + DateToEntriesMap::const_iterator end = _entriesByDate->end(); + for (DateToEntriesMap::const_iterator it = _entriesByDate->begin(); it != end; ++it) + dateKeys.append(it->first); + + std::sort(dateKeys.begin(), dateKeys.end()); + for (int dateIndex = dateKeys.size() - 1; dateIndex >= 0; dateIndex--) { + NSArray *entries = _entriesByDate->get(dateKeys[dateIndex]).get(); + int entryCount = [entries count]; + for (int entryIndex = 0; entryIndex < entryCount; ++entryIndex) + [arrayRep addObject:[[entries objectAtIndex:entryIndex] dictionaryRepresentation]]; + } + + return arrayRep; +} + +- (BOOL)loadHistoryGutsFromURL:(NSURL *)URL savedItemsCount:(int *)numberOfItemsLoaded collectDiscardedItemsInto:(NSMutableArray *)discardedItems error:(NSError **)error +{ + *numberOfItemsLoaded = 0; + NSDictionary *dictionary = nil; + + // Optimize loading from local file, which is faster than using the general URL loading mechanism + if ([URL isFileURL]) { + dictionary = [NSDictionary dictionaryWithContentsOfFile:[URL path]]; + if (!dictionary) { +#if !LOG_DISABLED + if ([[NSFileManager defaultManager] fileExistsAtPath:[URL path]]) + LOG_ERROR("unable to read history from file %@; perhaps contents are corrupted", [URL path]); +#endif + // else file doesn't exist, which is normal the first time + return NO; + } + } else { + NSData *data = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:URL] returningResponse:nil error:error]; + if (data && [data length] > 0) { + dictionary = [NSPropertyListSerialization propertyListFromData:data + mutabilityOption:NSPropertyListImmutable + format:nil + errorDescription:nil]; + } + } + + // We used to support NSArrays here, but that was before Safari 1.0 shipped. We will no longer support + // that ancient format, so anything that isn't an NSDictionary is bogus. + if (![dictionary isKindOfClass:[NSDictionary class]]) + return NO; + + NSNumber *fileVersionObject = [dictionary objectForKey:FileVersionKey]; + int fileVersion; + // we don't trust data obtained from elsewhere, so double-check + if (!fileVersionObject || ![fileVersionObject isKindOfClass:[NSNumber class]]) { + LOG_ERROR("history file version can't be determined, therefore not loading"); + return NO; + } + fileVersion = [fileVersionObject intValue]; + if (fileVersion > currentFileVersion) { + LOG_ERROR("history file version is %d, newer than newest known version %d, therefore not loading", fileVersion, currentFileVersion); + return NO; + } + + NSArray *array = [dictionary objectForKey:DatesArrayKey]; + + int itemCountLimit = [self historyItemLimit]; + NSTimeInterval ageLimitDate = [[self ageLimitDate] timeIntervalSinceReferenceDate]; + NSEnumerator *enumerator = [array objectEnumerator]; + BOOL ageLimitPassed = NO; + BOOL itemLimitPassed = NO; + ASSERT(*numberOfItemsLoaded == 0); + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSDictionary *itemAsDictionary; + while ((itemAsDictionary = [enumerator nextObject]) != nil) { + WebHistoryItem *item = [[WebHistoryItem alloc] initFromDictionaryRepresentation:itemAsDictionary]; + + // item without URL is useless; data on disk must have been bad; ignore + if ([item URLString]) { + // Test against date limit. Since the items are ordered newest to oldest, we can stop comparing + // once we've found the first item that's too old. + if (!ageLimitPassed && [item lastVisitedTimeInterval] <= ageLimitDate) + ageLimitPassed = YES; + + if (ageLimitPassed || itemLimitPassed) + [discardedItems addObject:item]; + else { + [self addItem:item]; + ++(*numberOfItemsLoaded); + if (*numberOfItemsLoaded == itemCountLimit) + itemLimitPassed = YES; + + // Draining the autorelease pool every 50 iterations was found by experimentation to be optimal + if (*numberOfItemsLoaded % 50 == 0) { + [pool drain]; + pool = [[NSAutoreleasePool alloc] init]; + } + } + } + [item release]; + } + [pool drain]; + + return YES; +} + +- (BOOL)loadFromURL:(NSURL *)URL collectDiscardedItemsInto:(NSMutableArray *)discardedItems error:(NSError **)error +{ +#if !LOG_DISABLED + double start = CFAbsoluteTimeGetCurrent(); +#endif + + int numberOfItems; + if (![self loadHistoryGutsFromURL:URL savedItemsCount:&numberOfItems collectDiscardedItemsInto:discardedItems error:error]) + return NO; + +#if !LOG_DISABLED + double duration = CFAbsoluteTimeGetCurrent() - start; + LOG(Timing, "loading %d history entries from %@ took %f seconds", numberOfItems, URL, duration); +#endif + + return YES; +} + +- (BOOL)saveHistoryGuts:(int *)numberOfItemsSaved URL:(NSURL *)URL error:(NSError **)error +{ + *numberOfItemsSaved = 0; + + NSArray *array = [self arrayRepresentation]; + NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys: + array, DatesArrayKey, + [NSNumber numberWithInt:currentFileVersion], FileVersionKey, + nil]; + NSData *data = [NSPropertyListSerialization dataFromPropertyList:dictionary format:NSPropertyListBinaryFormat_v1_0 errorDescription:nil]; + if (![data writeToURL:URL options:0 error:error]) { + LOG_ERROR("attempt to save %@ to %@ failed", dictionary, URL); + return NO; + } + + *numberOfItemsSaved = [array count]; + return YES; +} + +- (BOOL)saveToURL:(NSURL *)URL error:(NSError **)error +{ +#if !LOG_DISABLED + double start = CFAbsoluteTimeGetCurrent(); +#endif + + int numberOfItems; + if (![self saveHistoryGuts:&numberOfItems URL:URL error:error]) + return NO; + +#if !LOG_DISABLED + double duration = CFAbsoluteTimeGetCurrent() - start; + LOG(Timing, "saving %d history entries to %@ took %f seconds", numberOfItems, URL, duration); +#endif + + return YES; +} + +- (void)addVisitedLinksToPageGroup:(PageGroup&)group +{ + NSEnumerator *enumerator = [_entriesByURL keyEnumerator]; + while (NSString *url = [enumerator nextObject]) { + size_t length = [url length]; + const UChar* characters = CFStringGetCharactersPtr(reinterpret_cast<CFStringRef>(url)); + if (characters) + group.addVisitedLink(characters, length); + else { + Vector<UChar, 512> buffer(length); + [url getCharacters:buffer.data()]; + group.addVisitedLink(buffer.data(), length); + } + } +} + +@end + +@implementation WebHistory + ++ (WebHistory *)optionalSharedHistory +{ + return _sharedHistory; +} + ++ (void)setOptionalSharedHistory:(WebHistory *)history +{ + if (_sharedHistory == history) + return; + // FIXME: Need to think about multiple instances of WebHistory per application + // and correct synchronization of history file between applications. + [_sharedHistory release]; + _sharedHistory = [history retain]; + PageGroup::setShouldTrackVisitedLinks(history); + PageGroup::removeAllVisitedLinks(); +} + +- (id)init +{ + self = [super init]; + if (!self) + return nil; + _historyPrivate = [[WebHistoryPrivate alloc] init]; + return self; +} + +- (void)dealloc +{ + [_historyPrivate release]; + [super dealloc]; +} + +#pragma mark MODIFYING CONTENTS + +- (void)_sendNotification:(NSString *)name entries:(NSArray *)entries +{ + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:entries, WebHistoryItemsKey, nil]; + [[NSNotificationCenter defaultCenter] + postNotificationName:name object:self userInfo:userInfo]; +} + +- (void)removeItems:(NSArray *)entries +{ + if ([_historyPrivate removeItems:entries]) { + [self _sendNotification:WebHistoryItemsRemovedNotification + entries:entries]; + } +} + +- (void)removeAllItems +{ + if ([_historyPrivate removeAllItems]) { + [[NSNotificationCenter defaultCenter] + postNotificationName:WebHistoryAllItemsRemovedNotification + object:self]; + } +} + +- (void)addItems:(NSArray *)newEntries +{ + [_historyPrivate addItems:newEntries]; + [self _sendNotification:WebHistoryItemsAddedNotification + entries:newEntries]; +} + +#pragma mark DATE-BASED RETRIEVAL + +- (NSArray *)orderedLastVisitedDays +{ + return [_historyPrivate orderedLastVisitedDays]; +} + +- (NSArray *)orderedItemsLastVisitedOnDay:(NSCalendarDate *)date +{ + return [_historyPrivate orderedItemsLastVisitedOnDay:date]; +} + +#pragma mark URL MATCHING + +- (BOOL)containsURL:(NSURL *)URL +{ + return [_historyPrivate containsURL:URL]; +} + +- (WebHistoryItem *)itemForURL:(NSURL *)URL +{ + return [_historyPrivate itemForURL:URL]; +} + +#pragma mark SAVING TO DISK + +- (BOOL)loadFromURL:(NSURL *)URL error:(NSError **)error +{ + NSMutableArray *discardedItems = [[NSMutableArray alloc] init]; + if (![_historyPrivate loadFromURL:URL collectDiscardedItemsInto:discardedItems error:error]) { + [discardedItems release]; + return NO; + } + + [[NSNotificationCenter defaultCenter] + postNotificationName:WebHistoryLoadedNotification + object:self]; + + if ([discardedItems count]) + [self _sendNotification:WebHistoryItemsDiscardedWhileLoadingNotification entries:discardedItems]; + + [discardedItems release]; + return YES; +} + +- (BOOL)saveToURL:(NSURL *)URL error:(NSError **)error +{ + if (![_historyPrivate saveToURL:URL error:error]) + return NO; + [[NSNotificationCenter defaultCenter] + postNotificationName:WebHistorySavedNotification + object:self]; + return YES; +} + +- (void)setHistoryItemLimit:(int)limit +{ + [_historyPrivate setHistoryItemLimit:limit]; +} + +- (int)historyItemLimit +{ + return [_historyPrivate historyItemLimit]; +} + +- (void)setHistoryAgeInDaysLimit:(int)limit +{ + [_historyPrivate setHistoryAgeInDaysLimit:limit]; +} + +- (int)historyAgeInDaysLimit +{ + return [_historyPrivate historyAgeInDaysLimit]; +} + +@end + +@implementation WebHistory (WebPrivate) + +- (WebHistoryItem *)_itemForURLString:(NSString *)URLString +{ + return [_historyPrivate itemForURLString:URLString]; +} + +@end + +@implementation WebHistory (WebInternal) + +- (void)_addItemForURL:(NSURL *)URL title:(NSString *)title +{ + WebHistoryItem *entry = [[WebHistoryItem alloc] initWithURL:URL title:title]; + [entry _setLastVisitedTimeInterval:[NSDate timeIntervalSinceReferenceDate]]; + + LOG(History, "adding %@", entry); + [_historyPrivate addItem:entry]; + [self _sendNotification:WebHistoryItemsAddedNotification + entries:[NSArray arrayWithObject:entry]]; + + [entry release]; +} + +- (void)_addVisitedLinksToPageGroup:(WebCore::PageGroup&)group +{ + [_historyPrivate addVisitedLinksToPageGroup:group]; +} + +@end |