📄 webhistory.mm
字号:
}- (WebHistoryItem *)itemForURL:(NSURL *)URL{ return [self itemForURLString:[URL _web_originalDataAsString]];}- (NSArray *)allItems{ return [_entriesByURL allValues];}#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 { if ([self addItem:item discardDuplicate:YES]) ++(*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{ NSArray *entries = [_historyPrivate allItems]; if ([_historyPrivate removeAllItems]) [self _sendNotification:WebHistoryAllItemsRemovedNotification entries:entries];}- (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];}- (NSArray *)allItems{ return [_historyPrivate allItems];}@end@implementation WebHistory (WebInternal)- (void)_visitedURL:(NSURL *)url withTitle:(NSString *)title method:(NSString *)method wasFailure:(BOOL)wasFailure{ WebHistoryItem *entry = [_historyPrivate visitedURL:url withTitle:title]; HistoryItem* item = core(entry); item->setLastVisitWasFailure(wasFailure); if ([method length]) item->setLastVisitWasHTTPNonGet([method caseInsensitiveCompare:@"GET"] && (![[url scheme] caseInsensitiveCompare:@"http"] || ![[url scheme] caseInsensitiveCompare:@"https"])); item->setRedirectURLs(std::auto_ptr<Vector<String> >()); NSArray *entries = [[NSArray alloc] initWithObjects:entry, nil]; [self _sendNotification:WebHistoryItemsAddedNotification entries:entries]; [entries release];}- (void)_addVisitedLinksToPageGroup:(WebCore::PageGroup&)group{ [_historyPrivate addVisitedLinksToPageGroup:group];}@end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -