2 * Copyright (C) 2005, 2008, 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #import "WebHistoryInternal.h"
31 #import "WebHistoryItemInternal.h"
32 #import "WebKitLogging.h"
33 #import "WebNSURLExtras.h"
34 #import "WebTypesInternal.h"
35 #import <WebCore/HistoryItem.h>
36 #import <WebCore/HistoryPropertyList.h>
37 #import <WebCore/PageGroup.h>
39 using namespace WebCore;
42 typedef int64_t WebHistoryDateKey;
43 typedef HashMap<WebHistoryDateKey, RetainPtr<NSMutableArray> > DateToEntriesMap;
45 NSString *WebHistoryItemsAddedNotification = @"WebHistoryItemsAddedNotification";
46 NSString *WebHistoryItemsRemovedNotification = @"WebHistoryItemsRemovedNotification";
47 NSString *WebHistoryAllItemsRemovedNotification = @"WebHistoryAllItemsRemovedNotification";
48 NSString *WebHistoryLoadedNotification = @"WebHistoryLoadedNotification";
49 NSString *WebHistoryItemsDiscardedWhileLoadingNotification = @"WebHistoryItemsDiscardedWhileLoadingNotification";
50 NSString *WebHistorySavedNotification = @"WebHistorySavedNotification";
51 NSString *WebHistoryItemsKey = @"WebHistoryItems";
53 static WebHistory *_sharedHistory = nil;
55 NSString *FileVersionKey = @"WebHistoryFileVersion";
56 NSString *DatesArrayKey = @"WebHistoryDates";
58 #define currentFileVersion 1
60 class WebHistoryWriter : public HistoryPropertyListWriter {
62 WebHistoryWriter(DateToEntriesMap*);
65 virtual void writeHistoryItems(BinaryPropertyListObjectStream&);
67 DateToEntriesMap* m_entriesByDate;
68 Vector<int> m_dateKeys;
71 @interface WebHistoryPrivate : NSObject {
73 NSMutableDictionary *_entriesByURL;
74 DateToEntriesMap* _entriesByDate;
75 NSMutableArray *_orderedLastVisitedDays;
78 BOOL ageInDaysLimitSet;
82 - (WebHistoryItem *)visitedURL:(NSURL *)url withTitle:(NSString *)title increaseVisitCount:(BOOL)increaseVisitCount;
84 - (BOOL)addItem:(WebHistoryItem *)entry discardDuplicate:(BOOL)discardDuplicate;
85 - (void)addItems:(NSArray *)newEntries;
86 - (BOOL)removeItem:(WebHistoryItem *)entry;
87 - (BOOL)removeItems:(NSArray *)entries;
88 - (BOOL)removeAllItems;
90 - (NSArray *)orderedLastVisitedDays;
91 - (NSArray *)orderedItemsLastVisitedOnDay:(NSCalendarDate *)calendarDate;
92 - (BOOL)containsURL:(NSURL *)URL;
93 - (WebHistoryItem *)itemForURL:(NSURL *)URL;
94 - (WebHistoryItem *)itemForURLString:(NSString *)URLString;
95 - (NSArray *)allItems;
97 - (BOOL)loadFromURL:(NSURL *)URL collectDiscardedItemsInto:(NSMutableArray *)discardedItems error:(NSError **)error;
98 - (BOOL)saveToURL:(NSURL *)URL error:(NSError **)error;
100 - (NSCalendarDate *)ageLimitDate;
102 - (void)setHistoryItemLimit:(int)limit;
103 - (int)historyItemLimit;
104 - (void)setHistoryAgeInDaysLimit:(int)limit;
105 - (int)historyAgeInDaysLimit;
107 - (void)addVisitedLinksToPageGroup:(PageGroup&)group;
111 @implementation WebHistoryPrivate
113 // MARK: OBJECT FRAMEWORK
117 [[NSUserDefaults standardUserDefaults] registerDefaults:
118 [NSDictionary dictionaryWithObjectsAndKeys:
119 @"1000", @"WebKitHistoryItemLimit",
120 @"7", @"WebKitHistoryAgeInDaysLimit",
130 _entriesByURL = [[NSMutableDictionary alloc] init];
131 _entriesByDate = new DateToEntriesMap;
138 [_entriesByURL release];
139 [_orderedLastVisitedDays release];
140 delete _entriesByDate;
146 delete _entriesByDate;
150 // MARK: MODIFYING CONTENTS
152 static void getDayBoundaries(NSTimeInterval interval, NSTimeInterval& beginningOfDay, NSTimeInterval& beginningOfNextDay)
154 CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
155 CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(interval, timeZone);
159 beginningOfDay = CFGregorianDateGetAbsoluteTime(date, timeZone);
161 beginningOfNextDay = CFGregorianDateGetAbsoluteTime(date, timeZone);
165 static inline NSTimeInterval beginningOfDay(NSTimeInterval date)
167 static NSTimeInterval cachedBeginningOfDay = NAN;
168 static NSTimeInterval cachedBeginningOfNextDay;
169 if (!(date >= cachedBeginningOfDay && date < cachedBeginningOfNextDay))
170 getDayBoundaries(date, cachedBeginningOfDay, cachedBeginningOfNextDay);
171 return cachedBeginningOfDay;
174 static inline WebHistoryDateKey dateKey(NSTimeInterval date)
176 // Converting from double (NSTimeInterval) to int64_t (WebHistoryDateKey) is
177 // safe here because all sensible dates are in the range -2**48 .. 2**47 which
178 // safely fits in an int64_t.
179 return beginningOfDay(date);
182 // Returns whether the day is already in the list of days,
183 // and fills in *key with the key used to access its location
184 - (BOOL)findKey:(WebHistoryDateKey*)key forDay:(NSTimeInterval)date
186 ASSERT_ARG(key, key);
187 *key = dateKey(date);
188 return _entriesByDate->contains(*key);
191 - (void)insertItem:(WebHistoryItem *)entry forDateKey:(WebHistoryDateKey)dateKey
193 ASSERT_ARG(entry, entry != nil);
194 ASSERT(_entriesByDate->contains(dateKey));
196 NSMutableArray *entriesForDate = _entriesByDate->get(dateKey).get();
197 NSTimeInterval entryDate = [entry lastVisitedTimeInterval];
199 unsigned count = [entriesForDate count];
201 // The entries for each day are stored in a sorted array with the most recent entry first
202 // Check for the common cases of the entry being newer than all existing entries or the first entry of the day
203 if (!count || [[entriesForDate objectAtIndex:0] lastVisitedTimeInterval] < entryDate) {
204 [entriesForDate insertObject:entry atIndex:0];
207 // .. or older than all existing entries
208 if (count > 0 && [[entriesForDate objectAtIndex:count - 1] lastVisitedTimeInterval] >= entryDate) {
209 [entriesForDate insertObject:entry atIndex:count];
214 unsigned high = count;
216 unsigned mid = low + (high - low) / 2;
217 if ([[entriesForDate objectAtIndex:mid] lastVisitedTimeInterval] >= entryDate)
223 // low is now the index of the first entry that is older than entryDate
224 [entriesForDate insertObject:entry atIndex:low];
227 - (BOOL)removeItemFromDateCaches:(WebHistoryItem *)entry
229 WebHistoryDateKey dateKey;
230 BOOL foundDate = [self findKey:&dateKey forDay:[entry lastVisitedTimeInterval]];
235 DateToEntriesMap::iterator it = _entriesByDate->find(dateKey);
236 NSMutableArray *entriesForDate = it->second.get();
237 [entriesForDate removeObjectIdenticalTo:entry];
239 // remove this date entirely if there are no other entries on it
240 if ([entriesForDate count] == 0) {
241 _entriesByDate->remove(it);
242 // Clear _orderedLastVisitedDays so it will be regenerated when next requested.
243 [_orderedLastVisitedDays release];
244 _orderedLastVisitedDays = nil;
250 - (BOOL)removeItemForURLString:(NSString *)URLString
252 WebHistoryItem *entry = [_entriesByURL objectForKey:URLString];
256 [_entriesByURL removeObjectForKey:URLString];
259 [self removeItemFromDateCaches:entry];
261 BOOL itemWasInDateCaches = [self removeItemFromDateCaches:entry];
262 ASSERT(itemWasInDateCaches);
265 if (![_entriesByURL count])
266 PageGroup::removeAllVisitedLinks();
271 - (void)addItemToDateCaches:(WebHistoryItem *)entry
273 WebHistoryDateKey dateKey;
274 if ([self findKey:&dateKey forDay:[entry lastVisitedTimeInterval]])
275 // other entries already exist for this date
276 [self insertItem:entry forDateKey:dateKey];
278 // no other entries exist for this date
279 NSMutableArray *entries = [[NSMutableArray alloc] initWithObjects:&entry count:1];
280 _entriesByDate->set(dateKey, entries);
282 // Clear _orderedLastVisitedDays so it will be regenerated when next requested.
283 [_orderedLastVisitedDays release];
284 _orderedLastVisitedDays = nil;
288 - (WebHistoryItem *)visitedURL:(NSURL *)url withTitle:(NSString *)title increaseVisitCount:(BOOL)increaseVisitCount
293 NSString *URLString = [url _web_originalDataAsString];
294 WebHistoryItem *entry = [_entriesByURL objectForKey:URLString];
297 LOG(History, "Updating global history entry %@", entry);
298 // Remove the item from date caches before changing its last visited date. Otherwise we might get duplicate entries
299 // as seen in <rdar://problem/6570573>.
300 BOOL itemWasInDateCaches = [self removeItemFromDateCaches:entry];
301 ASSERT_UNUSED(itemWasInDateCaches, itemWasInDateCaches);
303 [entry _visitedWithTitle:title increaseVisitCount:increaseVisitCount];
305 LOG(History, "Adding new global history entry for %@", url);
306 entry = [[WebHistoryItem alloc] initWithURLString:URLString title:title lastVisitedTimeInterval:[NSDate timeIntervalSinceReferenceDate]];
307 [entry _recordInitialVisit];
308 [_entriesByURL setObject:entry forKey:URLString];
312 [self addItemToDateCaches:entry];
317 - (BOOL)addItem:(WebHistoryItem *)entry discardDuplicate:(BOOL)discardDuplicate
319 ASSERT_ARG(entry, entry);
320 ASSERT_ARG(entry, [entry lastVisitedTimeInterval] != 0);
322 NSString *URLString = [entry URLString];
324 WebHistoryItem *oldEntry = [_entriesByURL objectForKey:URLString];
326 if (discardDuplicate)
329 // The last reference to oldEntry might be this dictionary, so we hold onto a reference
330 // until we're done with oldEntry.
332 [self removeItemForURLString:URLString];
334 // If we already have an item with this URL, we need to merge info that drives the
335 // URL autocomplete heuristics from that item into the new one.
336 [entry _mergeAutoCompleteHints:oldEntry];
340 [self addItemToDateCaches:entry];
341 [_entriesByURL setObject:entry forKey:URLString];
346 - (BOOL)removeItem:(WebHistoryItem *)entry
348 NSString *URLString = [entry URLString];
350 // If this exact object isn't stored, then make no change.
351 // FIXME: Is this the right behavior if this entry isn't present, but another entry for the same URL is?
352 // Maybe need to change the API to make something like removeEntryForURLString public instead.
353 WebHistoryItem *matchingEntry = [_entriesByURL objectForKey:URLString];
354 if (matchingEntry != entry)
357 [self removeItemForURLString:URLString];
362 - (BOOL)removeItems:(NSArray *)entries
364 NSUInteger count = [entries count];
368 for (NSUInteger index = 0; index < count; ++index)
369 [self removeItem:[entries objectAtIndex:index]];
374 - (BOOL)removeAllItems
376 if (_entriesByDate->isEmpty())
379 _entriesByDate->clear();
380 [_entriesByURL removeAllObjects];
382 // Clear _orderedLastVisitedDays so it will be regenerated when next requested.
383 [_orderedLastVisitedDays release];
384 _orderedLastVisitedDays = nil;
386 PageGroup::removeAllVisitedLinks();
391 - (void)addItems:(NSArray *)newEntries
393 // There is no guarantee that the incoming entries are in any particular
394 // order, but if this is called with a set of entries that were created by
395 // iterating through the results of orderedLastVisitedDays and orderedItemsLastVisitedOnDayy
396 // then they will be ordered chronologically from newest to oldest. We can make adding them
397 // faster (fewer compares) by inserting them from oldest to newest.
398 NSEnumerator *enumerator = [newEntries reverseObjectEnumerator];
399 while (WebHistoryItem *entry = [enumerator nextObject])
400 [self addItem:entry discardDuplicate:NO];
403 // MARK: DATE-BASED RETRIEVAL
405 - (NSArray *)orderedLastVisitedDays
407 if (!_orderedLastVisitedDays) {
408 Vector<int> daysAsTimeIntervals;
409 daysAsTimeIntervals.reserveCapacity(_entriesByDate->size());
410 DateToEntriesMap::const_iterator end = _entriesByDate->end();
411 for (DateToEntriesMap::const_iterator it = _entriesByDate->begin(); it != end; ++it)
412 daysAsTimeIntervals.append(it->first);
414 sort(daysAsTimeIntervals.begin(), daysAsTimeIntervals.end());
415 size_t count = daysAsTimeIntervals.size();
416 _orderedLastVisitedDays = [[NSMutableArray alloc] initWithCapacity:count];
417 for (int i = count - 1; i >= 0; i--) {
418 NSTimeInterval interval = daysAsTimeIntervals[i];
419 NSCalendarDate *date = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:interval];
420 [_orderedLastVisitedDays addObject:date];
424 return _orderedLastVisitedDays;
427 - (NSArray *)orderedItemsLastVisitedOnDay:(NSCalendarDate *)date
429 WebHistoryDateKey dateKey;
430 if (![self findKey:&dateKey forDay:[date timeIntervalSinceReferenceDate]])
432 return _entriesByDate->get(dateKey).get();
435 // MARK: URL MATCHING
437 - (WebHistoryItem *)itemForURLString:(NSString *)URLString
439 return [_entriesByURL objectForKey:URLString];
442 - (BOOL)containsURL:(NSURL *)URL
444 return [self itemForURLString:[URL _web_originalDataAsString]] != nil;
447 - (WebHistoryItem *)itemForURL:(NSURL *)URL
449 return [self itemForURLString:[URL _web_originalDataAsString]];
452 - (NSArray *)allItems
454 return [_entriesByURL allValues];
457 // MARK: ARCHIVING/UNARCHIVING
459 - (void)setHistoryAgeInDaysLimit:(int)limit
461 ageInDaysLimitSet = YES;
462 ageInDaysLimit = limit;
465 - (int)historyAgeInDaysLimit
467 if (ageInDaysLimitSet)
468 return ageInDaysLimit;
469 return [[NSUserDefaults standardUserDefaults] integerForKey:@"WebKitHistoryAgeInDaysLimit"];
472 - (void)setHistoryItemLimit:(int)limit
478 - (int)historyItemLimit
482 return [[NSUserDefaults standardUserDefaults] integerForKey:@"WebKitHistoryItemLimit"];
485 // Return a date that marks the age limit for history entries saved to or
486 // loaded from disk. Any entry older than this item should be rejected.
487 - (NSCalendarDate *)ageLimitDate
489 return [[NSCalendarDate calendarDate] dateByAddingYears:0 months:0 days:-[self historyAgeInDaysLimit]
490 hours:0 minutes:0 seconds:0];
493 - (BOOL)loadHistoryGutsFromURL:(NSURL *)URL savedItemsCount:(int *)numberOfItemsLoaded collectDiscardedItemsInto:(NSMutableArray *)discardedItems error:(NSError **)error
495 *numberOfItemsLoaded = 0;
496 NSDictionary *dictionary = nil;
498 // Optimize loading from local file, which is faster than using the general URL loading mechanism
499 if ([URL isFileURL]) {
500 dictionary = [NSDictionary dictionaryWithContentsOfFile:[URL path]];
503 if ([[NSFileManager defaultManager] fileExistsAtPath:[URL path]])
504 LOG_ERROR("unable to read history from file %@; perhaps contents are corrupted", [URL path]);
506 // else file doesn't exist, which is normal the first time
510 NSData *data = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:URL] returningResponse:nil error:error];
511 if (data && [data length] > 0) {
512 dictionary = [NSPropertyListSerialization propertyListFromData:data
513 mutabilityOption:NSPropertyListImmutable
515 errorDescription:nil];
519 // We used to support NSArrays here, but that was before Safari 1.0 shipped. We will no longer support
520 // that ancient format, so anything that isn't an NSDictionary is bogus.
521 if (![dictionary isKindOfClass:[NSDictionary class]])
524 NSNumber *fileVersionObject = [dictionary objectForKey:FileVersionKey];
526 // we don't trust data obtained from elsewhere, so double-check
527 if (!fileVersionObject || ![fileVersionObject isKindOfClass:[NSNumber class]]) {
528 LOG_ERROR("history file version can't be determined, therefore not loading");
531 fileVersion = [fileVersionObject intValue];
532 if (fileVersion > currentFileVersion) {
533 LOG_ERROR("history file version is %d, newer than newest known version %d, therefore not loading", fileVersion, currentFileVersion);
537 NSArray *array = [dictionary objectForKey:DatesArrayKey];
539 int itemCountLimit = [self historyItemLimit];
540 NSTimeInterval ageLimitDate = [[self ageLimitDate] timeIntervalSinceReferenceDate];
541 NSEnumerator *enumerator = [array objectEnumerator];
542 BOOL ageLimitPassed = NO;
543 BOOL itemLimitPassed = NO;
544 ASSERT(*numberOfItemsLoaded == 0);
546 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
547 NSDictionary *itemAsDictionary;
548 while ((itemAsDictionary = [enumerator nextObject]) != nil) {
549 WebHistoryItem *item = [[WebHistoryItem alloc] initFromDictionaryRepresentation:itemAsDictionary];
551 // item without URL is useless; data on disk must have been bad; ignore
552 if ([item URLString]) {
553 // Test against date limit. Since the items are ordered newest to oldest, we can stop comparing
554 // once we've found the first item that's too old.
555 if (!ageLimitPassed && [item lastVisitedTimeInterval] <= ageLimitDate)
556 ageLimitPassed = YES;
558 if (ageLimitPassed || itemLimitPassed)
559 [discardedItems addObject:item];
561 if ([self addItem:item discardDuplicate:YES])
562 ++(*numberOfItemsLoaded);
563 if (*numberOfItemsLoaded == itemCountLimit)
564 itemLimitPassed = YES;
566 // Draining the autorelease pool every 50 iterations was found by experimentation to be optimal
567 if (*numberOfItemsLoaded % 50 == 0) {
569 pool = [[NSAutoreleasePool alloc] init];
580 - (BOOL)loadFromURL:(NSURL *)URL collectDiscardedItemsInto:(NSMutableArray *)discardedItems error:(NSError **)error
583 double start = CFAbsoluteTimeGetCurrent();
587 if (![self loadHistoryGutsFromURL:URL savedItemsCount:&numberOfItems collectDiscardedItemsInto:discardedItems error:error])
591 double duration = CFAbsoluteTimeGetCurrent() - start;
592 LOG(Timing, "loading %d history entries from %@ took %f seconds", numberOfItems, URL, duration);
600 if (_entriesByDate->isEmpty()) {
601 static NSData *emptyHistoryData = (NSData *)CFDataCreate(0, 0, 0);
602 return emptyHistoryData;
605 // Ignores the date and item count limits; these are respected when loading instead of when saving, so
606 // that clients can learn of discarded items by listening to WebHistoryItemsDiscardedWhileLoadingNotification.
607 WebHistoryWriter writer(_entriesByDate);
608 writer.writePropertyList();
609 return [[(NSData *)writer.releaseData().get() retain] autorelease];
612 - (BOOL)saveToURL:(NSURL *)URL error:(NSError **)error
615 double start = CFAbsoluteTimeGetCurrent();
618 BOOL result = [[self data] writeToURL:URL options:0 error:error];
621 double duration = CFAbsoluteTimeGetCurrent() - start;
622 LOG(Timing, "saving history to %@ took %f seconds", URL, duration);
628 - (void)addVisitedLinksToPageGroup:(PageGroup&)group
630 NSEnumerator *enumerator = [_entriesByURL keyEnumerator];
631 while (NSString *url = [enumerator nextObject]) {
632 size_t length = [url length];
633 const UChar* characters = CFStringGetCharactersPtr(reinterpret_cast<CFStringRef>(url));
635 group.addVisitedLink(characters, length);
637 Vector<UChar, 512> buffer(length);
638 [url getCharacters:buffer.data()];
639 group.addVisitedLink(buffer.data(), length);
646 @implementation WebHistory
648 + (WebHistory *)optionalSharedHistory
650 return _sharedHistory;
653 + (void)setOptionalSharedHistory:(WebHistory *)history
655 if (_sharedHistory == history)
657 // FIXME: Need to think about multiple instances of WebHistory per application
658 // and correct synchronization of history file between applications.
659 [_sharedHistory release];
660 _sharedHistory = [history retain];
661 PageGroup::setShouldTrackVisitedLinks(history);
662 PageGroup::removeAllVisitedLinks();
670 _historyPrivate = [[WebHistoryPrivate alloc] init];
676 [_historyPrivate release];
680 // MARK: MODIFYING CONTENTS
682 - (void)_sendNotification:(NSString *)name entries:(NSArray *)entries
684 NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:entries, WebHistoryItemsKey, nil];
685 [[NSNotificationCenter defaultCenter]
686 postNotificationName:name object:self userInfo:userInfo];
689 - (void)removeItems:(NSArray *)entries
691 if ([_historyPrivate removeItems:entries]) {
692 [self _sendNotification:WebHistoryItemsRemovedNotification
697 - (void)removeAllItems
699 NSArray *entries = [_historyPrivate allItems];
700 if ([_historyPrivate removeAllItems])
701 [self _sendNotification:WebHistoryAllItemsRemovedNotification entries:entries];
704 - (void)addItems:(NSArray *)newEntries
706 [_historyPrivate addItems:newEntries];
707 [self _sendNotification:WebHistoryItemsAddedNotification
711 // MARK: DATE-BASED RETRIEVAL
713 - (NSArray *)orderedLastVisitedDays
715 return [_historyPrivate orderedLastVisitedDays];
718 - (NSArray *)orderedItemsLastVisitedOnDay:(NSCalendarDate *)date
720 return [_historyPrivate orderedItemsLastVisitedOnDay:date];
723 // MARK: URL MATCHING
725 - (BOOL)containsURL:(NSURL *)URL
727 return [_historyPrivate containsURL:URL];
730 - (WebHistoryItem *)itemForURL:(NSURL *)URL
732 return [_historyPrivate itemForURL:URL];
735 // MARK: SAVING TO DISK
737 - (BOOL)loadFromURL:(NSURL *)URL error:(NSError **)error
739 NSMutableArray *discardedItems = [[NSMutableArray alloc] init];
740 if (![_historyPrivate loadFromURL:URL collectDiscardedItemsInto:discardedItems error:error]) {
741 [discardedItems release];
745 [[NSNotificationCenter defaultCenter]
746 postNotificationName:WebHistoryLoadedNotification
749 if ([discardedItems count])
750 [self _sendNotification:WebHistoryItemsDiscardedWhileLoadingNotification entries:discardedItems];
752 [discardedItems release];
756 - (BOOL)saveToURL:(NSURL *)URL error:(NSError **)error
758 if (![_historyPrivate saveToURL:URL error:error])
760 [[NSNotificationCenter defaultCenter]
761 postNotificationName:WebHistorySavedNotification
766 - (void)setHistoryItemLimit:(int)limit
768 [_historyPrivate setHistoryItemLimit:limit];
771 - (int)historyItemLimit
773 return [_historyPrivate historyItemLimit];
776 - (void)setHistoryAgeInDaysLimit:(int)limit
778 [_historyPrivate setHistoryAgeInDaysLimit:limit];
781 - (int)historyAgeInDaysLimit
783 return [_historyPrivate historyAgeInDaysLimit];
788 @implementation WebHistory (WebPrivate)
790 - (WebHistoryItem *)_itemForURLString:(NSString *)URLString
792 return [_historyPrivate itemForURLString:URLString];
795 - (NSArray *)allItems
797 return [_historyPrivate allItems];
802 return [_historyPrivate data];
805 + (void)_setVisitedLinkTrackingEnabled:(BOOL)visitedLinkTrackingEnabled
807 PageGroup::setShouldTrackVisitedLinks(visitedLinkTrackingEnabled);
810 + (void)_removeAllVisitedLinks
812 PageGroup::removeAllVisitedLinks();
817 @implementation WebHistory (WebInternal)
819 - (void)_visitedURL:(NSURL *)url withTitle:(NSString *)title method:(NSString *)method wasFailure:(BOOL)wasFailure increaseVisitCount:(BOOL)increaseVisitCount
821 WebHistoryItem *entry = [_historyPrivate visitedURL:url withTitle:title increaseVisitCount:increaseVisitCount];
823 HistoryItem* item = core(entry);
824 item->setLastVisitWasFailure(wasFailure);
827 item->setLastVisitWasHTTPNonGet([method caseInsensitiveCompare:@"GET"] && (![[url scheme] caseInsensitiveCompare:@"http"] || ![[url scheme] caseInsensitiveCompare:@"https"]));
829 item->setRedirectURLs(nullptr);
831 NSArray *entries = [[NSArray alloc] initWithObjects:entry, nil];
832 [self _sendNotification:WebHistoryItemsAddedNotification entries:entries];
836 - (void)_addVisitedLinksToPageGroup:(WebCore::PageGroup&)group
838 [_historyPrivate addVisitedLinksToPageGroup:group];
843 WebHistoryWriter::WebHistoryWriter(DateToEntriesMap* entriesByDate)
844 : m_entriesByDate(entriesByDate)
846 m_dateKeys.reserveCapacity(m_entriesByDate->size());
847 DateToEntriesMap::const_iterator end = m_entriesByDate->end();
848 for (DateToEntriesMap::const_iterator it = m_entriesByDate->begin(); it != end; ++it)
849 m_dateKeys.append(it->first);
850 sort(m_dateKeys.begin(), m_dateKeys.end());
853 void WebHistoryWriter::writeHistoryItems(BinaryPropertyListObjectStream& stream)
855 for (int dateIndex = m_dateKeys.size() - 1; dateIndex >= 0; dateIndex--) {
856 NSArray *entries = m_entriesByDate->get(m_dateKeys[dateIndex]).get();
857 NSUInteger entryCount = [entries count];
858 for (NSUInteger entryIndex = 0; entryIndex < entryCount; ++entryIndex)
859 writeHistoryItem(stream, core([entries objectAtIndex:entryIndex]));