18e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project/*
20bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * Copyright (C) 2005, 2008, 2009 Apple Inc. All rights reserved.
38e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
48e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * Redistribution and use in source and binary forms, with or without
58e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * modification, are permitted provided that the following conditions
68e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * are met:
78e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
88e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * 1.  Redistributions of source code must retain the above copyright
98e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *     notice, this list of conditions and the following disclaimer.
108e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * 2.  Redistributions in binary form must reproduce the above copyright
118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *     notice, this list of conditions and the following disclaimer in the
128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *     documentation and/or other materials provided with the distribution.
138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *     its contributors may be used to endorse or promote products derived
158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *     from this software without specific prior written permission.
168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project */
288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#import "WebHistoryInternal.h"
308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#import "WebHistoryItemInternal.h"
328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#import "WebKitLogging.h"
338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#import "WebNSURLExtras.h"
348e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#import "WebTypesInternal.h"
35635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project#import <WebCore/HistoryItem.h>
360bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#import <WebCore/HistoryPropertyList.h>
378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#import <WebCore/PageGroup.h>
388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectusing namespace WebCore;
400bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochusing namespace std;
418e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projecttypedef int64_t WebHistoryDateKey;
438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projecttypedef HashMap<WebHistoryDateKey, RetainPtr<NSMutableArray> > DateToEntriesMap;
448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectNSString *WebHistoryItemsAddedNotification = @"WebHistoryItemsAddedNotification";
468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectNSString *WebHistoryItemsRemovedNotification = @"WebHistoryItemsRemovedNotification";
478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectNSString *WebHistoryAllItemsRemovedNotification = @"WebHistoryAllItemsRemovedNotification";
488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectNSString *WebHistoryLoadedNotification = @"WebHistoryLoadedNotification";
498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectNSString *WebHistoryItemsDiscardedWhileLoadingNotification = @"WebHistoryItemsDiscardedWhileLoadingNotification";
508e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectNSString *WebHistorySavedNotification = @"WebHistorySavedNotification";
518e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectNSString *WebHistoryItemsKey = @"WebHistoryItems";
528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectstatic WebHistory *_sharedHistory = nil;
548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
558e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectNSString *FileVersionKey = @"WebHistoryFileVersion";
568e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectNSString *DatesArrayKey = @"WebHistoryDates";
578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
588e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#define currentFileVersion 1
598e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
600bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochclass WebHistoryWriter : public HistoryPropertyListWriter {
610bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochpublic:
620bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    WebHistoryWriter(DateToEntriesMap*);
630bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
640bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochprivate:
650bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    virtual void writeHistoryItems(BinaryPropertyListObjectStream&);
660bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
670bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    DateToEntriesMap* m_entriesByDate;
680bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    Vector<int> m_dateKeys;
690bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch};
700bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
718e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project@interface WebHistoryPrivate : NSObject {
728e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project@private
738e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSMutableDictionary *_entriesByURL;
748e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    DateToEntriesMap* _entriesByDate;
758e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSMutableArray *_orderedLastVisitedDays;
768e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    BOOL itemLimitSet;
778e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    int itemLimit;
788e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    BOOL ageInDaysLimitSet;
798e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    int ageInDaysLimit;
808e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
818e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
820bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (WebHistoryItem *)visitedURL:(NSURL *)url withTitle:(NSString *)title increaseVisitCount:(BOOL)increaseVisitCount;
83635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
84635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project- (BOOL)addItem:(WebHistoryItem *)entry discardDuplicate:(BOOL)discardDuplicate;
858e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)addItems:(NSArray *)newEntries;
868e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)removeItem:(WebHistoryItem *)entry;
878e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)removeItems:(NSArray *)entries;
888e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)removeAllItems;
898e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
908e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (NSArray *)orderedLastVisitedDays;
918e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (NSArray *)orderedItemsLastVisitedOnDay:(NSCalendarDate *)calendarDate;
928e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)containsURL:(NSURL *)URL;
938e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (WebHistoryItem *)itemForURL:(NSURL *)URL;
948e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (WebHistoryItem *)itemForURLString:(NSString *)URLString;
95635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project- (NSArray *)allItems;
968e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
978e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)loadFromURL:(NSURL *)URL collectDiscardedItemsInto:(NSMutableArray *)discardedItems error:(NSError **)error;
988e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)saveToURL:(NSURL *)URL error:(NSError **)error;
998e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1008e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (NSCalendarDate *)ageLimitDate;
1018e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1028e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)setHistoryItemLimit:(int)limit;
1038e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (int)historyItemLimit;
1048e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)setHistoryAgeInDaysLimit:(int)limit;
1058e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (int)historyAgeInDaysLimit;
1068e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1078e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)addVisitedLinksToPageGroup:(PageGroup&)group;
1088e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1098e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project@end
1108e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project@implementation WebHistoryPrivate
1128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
113cad810f21b803229eb11403f9209855525a25d57Steve Block// MARK: OBJECT FRAMEWORK
1148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project+ (void)initialize
1168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
1178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [[NSUserDefaults standardUserDefaults] registerDefaults:
1188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [NSDictionary dictionaryWithObjectsAndKeys:
1198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            @"1000", @"WebKitHistoryItemLimit",
1208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            @"7", @"WebKitHistoryAgeInDaysLimit",
1218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            nil]];
1228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (id)init
1258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
1268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (![super init])
1278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return nil;
1288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    _entriesByURL = [[NSMutableDictionary alloc] init];
1308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    _entriesByDate = new DateToEntriesMap;
1318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return self;
1338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1348e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)dealloc
1368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
1378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [_entriesByURL release];
1388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [_orderedLastVisitedDays release];
1398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    delete _entriesByDate;
1408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [super dealloc];
1418e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)finalize
1448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
1458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    delete _entriesByDate;
1468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [super finalize];
1478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
149cad810f21b803229eb11403f9209855525a25d57Steve Block// MARK: MODIFYING CONTENTS
1508e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
151231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blockstatic void getDayBoundaries(NSTimeInterval interval, NSTimeInterval& beginningOfDay, NSTimeInterval& beginningOfNextDay)
1528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
1538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
1548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(interval, timeZone);
1558e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    date.hour = 0;
1568e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    date.minute = 0;
1578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    date.second = 0;
158231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    beginningOfDay = CFGregorianDateGetAbsoluteTime(date, timeZone);
159231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    date.day += 1;
160231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    beginningOfNextDay = CFGregorianDateGetAbsoluteTime(date, timeZone);
1618e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    CFRelease(timeZone);
162231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block}
1638e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
164231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blockstatic inline NSTimeInterval beginningOfDay(NSTimeInterval date)
165231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block{
166231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    static NSTimeInterval cachedBeginningOfDay = NAN;
167231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    static NSTimeInterval cachedBeginningOfNextDay;
168231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    if (!(date >= cachedBeginningOfDay && date < cachedBeginningOfNextDay))
169231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block        getDayBoundaries(date, cachedBeginningOfDay, cachedBeginningOfNextDay);
170231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    return cachedBeginningOfDay;
171231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block}
172231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block
173231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blockstatic inline WebHistoryDateKey dateKey(NSTimeInterval date)
174231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block{
175231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    // Converting from double (NSTimeInterval) to int64_t (WebHistoryDateKey) is
176231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    // safe here because all sensible dates are in the range -2**48 .. 2**47 which
177231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    // safely fits in an int64_t.
178231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    return beginningOfDay(date);
1798e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1808e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1818e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project// Returns whether the day is already in the list of days,
1828e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project// and fills in *key with the key used to access its location
1838e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)findKey:(WebHistoryDateKey*)key forDay:(NSTimeInterval)date
1848e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
185231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    ASSERT_ARG(key, key);
186231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    *key = dateKey(date);
1878e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return _entriesByDate->contains(*key);
1888e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1898e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1908e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)insertItem:(WebHistoryItem *)entry forDateKey:(WebHistoryDateKey)dateKey
1918e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
1928e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    ASSERT_ARG(entry, entry != nil);
1938e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    ASSERT(_entriesByDate->contains(dateKey));
1948e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1958e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSMutableArray *entriesForDate = _entriesByDate->get(dateKey).get();
1968e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSTimeInterval entryDate = [entry lastVisitedTimeInterval];
1978e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1988e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    unsigned count = [entriesForDate count];
1998e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2008e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // The entries for each day are stored in a sorted array with the most recent entry first
2018e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // Check for the common cases of the entry being newer than all existing entries or the first entry of the day
2028e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!count || [[entriesForDate objectAtIndex:0] lastVisitedTimeInterval] < entryDate) {
2038e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [entriesForDate insertObject:entry atIndex:0];
2048e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return;
2058e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
2068e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // .. or older than all existing entries
2078e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (count > 0 && [[entriesForDate objectAtIndex:count - 1] lastVisitedTimeInterval] >= entryDate) {
2088e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [entriesForDate insertObject:entry atIndex:count];
2098e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return;
2108e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
2118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    unsigned low = 0;
2138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    unsigned high = count;
2148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    while (low < high) {
2158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        unsigned mid = low + (high - low) / 2;
2168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        if ([[entriesForDate objectAtIndex:mid] lastVisitedTimeInterval] >= entryDate)
2178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            low = mid + 1;
2188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        else
2198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            high = mid;
2208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
2218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // low is now the index of the first entry that is older than entryDate
2238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [entriesForDate insertObject:entry atIndex:low];
2248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
2258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)removeItemFromDateCaches:(WebHistoryItem *)entry
2278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
2288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    WebHistoryDateKey dateKey;
2298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    BOOL foundDate = [self findKey:&dateKey forDay:[entry lastVisitedTimeInterval]];
2308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!foundDate)
2328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return NO;
2338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2348e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    DateToEntriesMap::iterator it = _entriesByDate->find(dateKey);
2358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSMutableArray *entriesForDate = it->second.get();
2368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [entriesForDate removeObjectIdenticalTo:entry];
2378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // remove this date entirely if there are no other entries on it
2398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if ([entriesForDate count] == 0) {
2408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        _entriesByDate->remove(it);
2418e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // Clear _orderedLastVisitedDays so it will be regenerated when next requested.
2428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [_orderedLastVisitedDays release];
2438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        _orderedLastVisitedDays = nil;
2448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
2458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return YES;
2478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
2488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)removeItemForURLString:(NSString *)URLString
2508e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
2518e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    WebHistoryItem *entry = [_entriesByURL objectForKey:URLString];
2528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!entry)
2538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return NO;
2548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2558e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [_entriesByURL removeObjectForKey:URLString];
2568e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#if ASSERT_DISABLED
2588e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [self removeItemFromDateCaches:entry];
2598e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#else
2608e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    BOOL itemWasInDateCaches = [self removeItemFromDateCaches:entry];
2618e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    ASSERT(itemWasInDateCaches);
2628e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#endif
2638e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2648e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (![_entriesByURL count])
2658e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        PageGroup::removeAllVisitedLinks();
2668e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2678e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return YES;
2688e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
2698e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2708e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)addItemToDateCaches:(WebHistoryItem *)entry
2718e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
2728e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    WebHistoryDateKey dateKey;
2738e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if ([self findKey:&dateKey forDay:[entry lastVisitedTimeInterval]])
2748e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // other entries already exist for this date
2758e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [self insertItem:entry forDateKey:dateKey];
2768e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    else {
2778e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // no other entries exist for this date
2788e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        NSMutableArray *entries = [[NSMutableArray alloc] initWithObjects:&entry count:1];
2798e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        _entriesByDate->set(dateKey, entries);
2808e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [entries release];
2818e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // Clear _orderedLastVisitedDays so it will be regenerated when next requested.
2828e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [_orderedLastVisitedDays release];
2838e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        _orderedLastVisitedDays = nil;
2848e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
2858e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
2868e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
2870bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (WebHistoryItem *)visitedURL:(NSURL *)url withTitle:(NSString *)title increaseVisitCount:(BOOL)increaseVisitCount
288635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
289635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    ASSERT(url);
290635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    ASSERT(title);
291635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
292635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    NSString *URLString = [url _web_originalDataAsString];
293635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    WebHistoryItem *entry = [_entriesByURL objectForKey:URLString];
294635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
295635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    if (entry) {
296635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        LOG(History, "Updating global history entry %@", entry);
297635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        // Remove the item from date caches before changing its last visited date.  Otherwise we might get duplicate entries
298635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        // as seen in <rdar://problem/6570573>.
299635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        BOOL itemWasInDateCaches = [self removeItemFromDateCaches:entry];
300635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        ASSERT_UNUSED(itemWasInDateCaches, itemWasInDateCaches);
301635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
3020bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        [entry _visitedWithTitle:title increaseVisitCount:increaseVisitCount];
303635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    } else {
304635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        LOG(History, "Adding new global history entry for %@", url);
305635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        entry = [[WebHistoryItem alloc] initWithURLString:URLString title:title lastVisitedTimeInterval:[NSDate timeIntervalSinceReferenceDate]];
306635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        [entry _recordInitialVisit];
307635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        [_entriesByURL setObject:entry forKey:URLString];
308635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        [entry release];
309635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    }
310635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
311635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    [self addItemToDateCaches:entry];
312635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
313635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    return entry;
314635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
315635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
316635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project- (BOOL)addItem:(WebHistoryItem *)entry discardDuplicate:(BOOL)discardDuplicate
3178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
3188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    ASSERT_ARG(entry, entry);
3198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    ASSERT_ARG(entry, [entry lastVisitedTimeInterval] != 0);
3208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSString *URLString = [entry URLString];
3228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    WebHistoryItem *oldEntry = [_entriesByURL objectForKey:URLString];
3248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (oldEntry) {
325635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        if (discardDuplicate)
326635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project            return NO;
327635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
3288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // The last reference to oldEntry might be this dictionary, so we hold onto a reference
3298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // until we're done with oldEntry.
3308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [oldEntry retain];
3318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [self removeItemForURLString:URLString];
3328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // If we already have an item with this URL, we need to merge info that drives the
3348e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // URL autocomplete heuristics from that item into the new one.
3358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [entry _mergeAutoCompleteHints:oldEntry];
3368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [oldEntry release];
3378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
3388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [self addItemToDateCaches:entry];
3408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [_entriesByURL setObject:entry forKey:URLString];
341635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
342635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    return YES;
3438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
3448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)removeItem:(WebHistoryItem *)entry
3468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
3478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSString *URLString = [entry URLString];
3488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // If this exact object isn't stored, then make no change.
3508e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // FIXME: Is this the right behavior if this entry isn't present, but another entry for the same URL is?
3518e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // Maybe need to change the API to make something like removeEntryForURLString public instead.
3528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    WebHistoryItem *matchingEntry = [_entriesByURL objectForKey:URLString];
3538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (matchingEntry != entry)
3548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return NO;
3558e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3568e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [self removeItemForURLString:URLString];
3578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3588e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return YES;
3598e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
3608e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3618e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)removeItems:(NSArray *)entries
3628e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
3638e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSUInteger count = [entries count];
3648e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!count)
3658e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return NO;
3668e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3678e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    for (NSUInteger index = 0; index < count; ++index)
3688e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [self removeItem:[entries objectAtIndex:index]];
3698e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3708e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return YES;
3718e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
3728e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3738e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)removeAllItems
3748e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
3758e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (_entriesByDate->isEmpty())
3768e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return NO;
3778e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3788e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    _entriesByDate->clear();
3798e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [_entriesByURL removeAllObjects];
3808e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3818e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // Clear _orderedLastVisitedDays so it will be regenerated when next requested.
3828e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [_orderedLastVisitedDays release];
3838e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    _orderedLastVisitedDays = nil;
3848e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3858e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    PageGroup::removeAllVisitedLinks();
3868e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3878e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return YES;
3888e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
3898e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3908e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)addItems:(NSArray *)newEntries
3918e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
3928e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // There is no guarantee that the incoming entries are in any particular
3938e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // order, but if this is called with a set of entries that were created by
3948e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // iterating through the results of orderedLastVisitedDays and orderedItemsLastVisitedOnDayy
3958e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // then they will be ordered chronologically from newest to oldest. We can make adding them
3968e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // faster (fewer compares) by inserting them from oldest to newest.
3978e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSEnumerator *enumerator = [newEntries reverseObjectEnumerator];
3988e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    while (WebHistoryItem *entry = [enumerator nextObject])
399635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        [self addItem:entry discardDuplicate:NO];
4008e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
4018e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
402cad810f21b803229eb11403f9209855525a25d57Steve Block// MARK: DATE-BASED RETRIEVAL
4038e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
4048e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (NSArray *)orderedLastVisitedDays
4058e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
4068e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!_orderedLastVisitedDays) {
4078e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        Vector<int> daysAsTimeIntervals;
4088e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        daysAsTimeIntervals.reserveCapacity(_entriesByDate->size());
4098e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        DateToEntriesMap::const_iterator end = _entriesByDate->end();
4108e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        for (DateToEntriesMap::const_iterator it = _entriesByDate->begin(); it != end; ++it)
4118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            daysAsTimeIntervals.append(it->first);
4128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
4130bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        sort(daysAsTimeIntervals.begin(), daysAsTimeIntervals.end());
4148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        size_t count = daysAsTimeIntervals.size();
4158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        _orderedLastVisitedDays = [[NSMutableArray alloc] initWithCapacity:count];
4168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        for (int i = count - 1; i >= 0; i--) {
4178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            NSTimeInterval interval = daysAsTimeIntervals[i];
4188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            NSCalendarDate *date = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:interval];
4198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            [_orderedLastVisitedDays addObject:date];
4208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            [date release];
4218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        }
4228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
4238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return _orderedLastVisitedDays;
4248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
4258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
4268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (NSArray *)orderedItemsLastVisitedOnDay:(NSCalendarDate *)date
4278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
4288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    WebHistoryDateKey dateKey;
4298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (![self findKey:&dateKey forDay:[date timeIntervalSinceReferenceDate]])
4308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return nil;
4318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return _entriesByDate->get(dateKey).get();
4328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
4338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
434cad810f21b803229eb11403f9209855525a25d57Steve Block// MARK: URL MATCHING
4358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
4368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (WebHistoryItem *)itemForURLString:(NSString *)URLString
4378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
4388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return [_entriesByURL objectForKey:URLString];
4398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
4408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
4418e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)containsURL:(NSURL *)URL
4428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
4438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return [self itemForURLString:[URL _web_originalDataAsString]] != nil;
4448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
4458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
4468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (WebHistoryItem *)itemForURL:(NSURL *)URL
4478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
4488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return [self itemForURLString:[URL _web_originalDataAsString]];
4498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
4508e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
451635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project- (NSArray *)allItems
452635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
453635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    return [_entriesByURL allValues];
454635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
455635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
456cad810f21b803229eb11403f9209855525a25d57Steve Block// MARK: ARCHIVING/UNARCHIVING
4578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
4588e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)setHistoryAgeInDaysLimit:(int)limit
4598e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
4608e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    ageInDaysLimitSet = YES;
4618e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    ageInDaysLimit = limit;
4628e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
4638e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
4648e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (int)historyAgeInDaysLimit
4658e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
4668e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (ageInDaysLimitSet)
4678e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return ageInDaysLimit;
4688e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return [[NSUserDefaults standardUserDefaults] integerForKey:@"WebKitHistoryAgeInDaysLimit"];
4698e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
4708e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
4718e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)setHistoryItemLimit:(int)limit
4728e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
4738e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    itemLimitSet = YES;
4748e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    itemLimit = limit;
4758e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
4768e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
4778e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (int)historyItemLimit
4788e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
4798e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (itemLimitSet)
4808e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return itemLimit;
4818e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return [[NSUserDefaults standardUserDefaults] integerForKey:@"WebKitHistoryItemLimit"];
4828e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
4838e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
4848e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project// Return a date that marks the age limit for history entries saved to or
4858e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project// loaded from disk. Any entry older than this item should be rejected.
4868e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (NSCalendarDate *)ageLimitDate
4878e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
4888e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return [[NSCalendarDate calendarDate] dateByAddingYears:0 months:0 days:-[self historyAgeInDaysLimit]
4898e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                                                      hours:0 minutes:0 seconds:0];
4908e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
4918e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
4928e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)loadHistoryGutsFromURL:(NSURL *)URL savedItemsCount:(int *)numberOfItemsLoaded collectDiscardedItemsInto:(NSMutableArray *)discardedItems error:(NSError **)error
4938e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
4948e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    *numberOfItemsLoaded = 0;
4958e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSDictionary *dictionary = nil;
4968e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
4978e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // Optimize loading from local file, which is faster than using the general URL loading mechanism
4988e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if ([URL isFileURL]) {
4998e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        dictionary = [NSDictionary dictionaryWithContentsOfFile:[URL path]];
5008e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        if (!dictionary) {
5018e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#if !LOG_DISABLED
5028e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            if ([[NSFileManager defaultManager] fileExistsAtPath:[URL path]])
5038e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                LOG_ERROR("unable to read history from file %@; perhaps contents are corrupted", [URL path]);
5048e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#endif
5058e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            // else file doesn't exist, which is normal the first time
5068e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            return NO;
5078e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        }
5088e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    } else {
5098e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        NSData *data = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:URL] returningResponse:nil error:error];
5108e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        if (data && [data length] > 0) {
5118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            dictionary = [NSPropertyListSerialization propertyListFromData:data
5128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                mutabilityOption:NSPropertyListImmutable
5138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                format:nil
5148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                errorDescription:nil];
5158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        }
5168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
5178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // We used to support NSArrays here, but that was before Safari 1.0 shipped. We will no longer support
5198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // that ancient format, so anything that isn't an NSDictionary is bogus.
5208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (![dictionary isKindOfClass:[NSDictionary class]])
5218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return NO;
5228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSNumber *fileVersionObject = [dictionary objectForKey:FileVersionKey];
5248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    int fileVersion;
5258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // we don't trust data obtained from elsewhere, so double-check
5268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!fileVersionObject || ![fileVersionObject isKindOfClass:[NSNumber class]]) {
5278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        LOG_ERROR("history file version can't be determined, therefore not loading");
5288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return NO;
5298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
5308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    fileVersion = [fileVersionObject intValue];
5318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (fileVersion > currentFileVersion) {
5328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        LOG_ERROR("history file version is %d, newer than newest known version %d, therefore not loading", fileVersion, currentFileVersion);
5338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return NO;
5348e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
5358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSArray *array = [dictionary objectForKey:DatesArrayKey];
5378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    int itemCountLimit = [self historyItemLimit];
5398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSTimeInterval ageLimitDate = [[self ageLimitDate] timeIntervalSinceReferenceDate];
5408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSEnumerator *enumerator = [array objectEnumerator];
5418e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    BOOL ageLimitPassed = NO;
5428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    BOOL itemLimitPassed = NO;
5438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    ASSERT(*numberOfItemsLoaded == 0);
5448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSDictionary *itemAsDictionary;
5478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    while ((itemAsDictionary = [enumerator nextObject]) != nil) {
5488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        WebHistoryItem *item = [[WebHistoryItem alloc] initFromDictionaryRepresentation:itemAsDictionary];
5498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5508e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        // item without URL is useless; data on disk must have been bad; ignore
5518e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        if ([item URLString]) {
5528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            // Test against date limit. Since the items are ordered newest to oldest, we can stop comparing
5538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            // once we've found the first item that's too old.
5548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            if (!ageLimitPassed && [item lastVisitedTimeInterval] <= ageLimitDate)
5558e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                ageLimitPassed = YES;
5568e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            if (ageLimitPassed || itemLimitPassed)
5588e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                [discardedItems addObject:item];
5598e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            else {
560635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project                if ([self addItem:item discardDuplicate:YES])
561635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project                    ++(*numberOfItemsLoaded);
5628e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                if (*numberOfItemsLoaded == itemCountLimit)
5638e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                    itemLimitPassed = YES;
5648e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5658e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                // Draining the autorelease pool every 50 iterations was found by experimentation to be optimal
5668e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                if (*numberOfItemsLoaded % 50 == 0) {
5678e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                    [pool drain];
5688e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                    pool = [[NSAutoreleasePool alloc] init];
5698e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                }
5708e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            }
5718e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        }
5728e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [item release];
5738e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
5748e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [pool drain];
5758e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5768e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return YES;
5778e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
5788e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5798e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)loadFromURL:(NSURL *)URL collectDiscardedItemsInto:(NSMutableArray *)discardedItems error:(NSError **)error
5808e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
5818e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#if !LOG_DISABLED
5828e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    double start = CFAbsoluteTimeGetCurrent();
5838e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#endif
5848e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5858e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    int numberOfItems;
5868e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (![self loadHistoryGutsFromURL:URL savedItemsCount:&numberOfItems collectDiscardedItemsInto:discardedItems error:error])
5878e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return NO;
5888e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5898e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#if !LOG_DISABLED
5908e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    double duration = CFAbsoluteTimeGetCurrent() - start;
5918e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    LOG(Timing, "loading %d history entries from %@ took %f seconds", numberOfItems, URL, duration);
5928e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#endif
5938e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5948e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return YES;
5958e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
5968e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
5970bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (NSData *)data
5988e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
5990bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    if (_entriesByDate->isEmpty()) {
6000bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        static NSData *emptyHistoryData = (NSData *)CFDataCreate(0, 0, 0);
6010bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        return emptyHistoryData;
6028e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
6030bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
6040bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    // Ignores the date and item count limits; these are respected when loading instead of when saving, so
6050bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    // that clients can learn of discarded items by listening to WebHistoryItemsDiscardedWhileLoadingNotification.
6060bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    WebHistoryWriter writer(_entriesByDate);
6070bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    writer.writePropertyList();
6080bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    return [[(NSData *)writer.releaseData().get() retain] autorelease];
6098e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
6108e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)saveToURL:(NSURL *)URL error:(NSError **)error
6128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
6138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#if !LOG_DISABLED
6148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    double start = CFAbsoluteTimeGetCurrent();
6158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#endif
6168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6170bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    BOOL result = [[self data] writeToURL:URL options:0 error:error];
6188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#if !LOG_DISABLED
6208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    double duration = CFAbsoluteTimeGetCurrent() - start;
6210bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    LOG(Timing, "saving history to %@ took %f seconds", URL, duration);
6228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#endif
6238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6240bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    return result;
6258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
6268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)addVisitedLinksToPageGroup:(PageGroup&)group
6288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
6298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSEnumerator *enumerator = [_entriesByURL keyEnumerator];
6308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    while (NSString *url = [enumerator nextObject]) {
6318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        size_t length = [url length];
6328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        const UChar* characters = CFStringGetCharactersPtr(reinterpret_cast<CFStringRef>(url));
6338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        if (characters)
6348e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            group.addVisitedLink(characters, length);
6358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        else {
6368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            Vector<UChar, 512> buffer(length);
6378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            [url getCharacters:buffer.data()];
6388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project            group.addVisitedLink(buffer.data(), length);
6398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        }
6408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
6418e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
6428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project@end
6448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project@implementation WebHistory
6468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project+ (WebHistory *)optionalSharedHistory
6488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
6498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return _sharedHistory;
6508e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
6518e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project+ (void)setOptionalSharedHistory:(WebHistory *)history
6538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
6548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (_sharedHistory == history)
6558e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return;
6568e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // FIXME: Need to think about multiple instances of WebHistory per application
6578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // and correct synchronization of history file between applications.
6588e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [_sharedHistory release];
6598e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    _sharedHistory = [history retain];
6608e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    PageGroup::setShouldTrackVisitedLinks(history);
6618e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    PageGroup::removeAllVisitedLinks();
6628e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
6638e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6648e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (id)init
6658e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
6668e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    self = [super init];
6678e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!self)
6688e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return nil;
6698e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    _historyPrivate = [[WebHistoryPrivate alloc] init];
6708e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return self;
6718e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
6728e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6738e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)dealloc
6748e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
6758e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [_historyPrivate release];
6768e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [super dealloc];
6778e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
6788e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
679cad810f21b803229eb11403f9209855525a25d57Steve Block// MARK: MODIFYING CONTENTS
6808e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6818e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)_sendNotification:(NSString *)name entries:(NSArray *)entries
6828e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
6838e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:entries, WebHistoryItemsKey, nil];
6848e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [[NSNotificationCenter defaultCenter]
6858e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        postNotificationName:name object:self userInfo:userInfo];
6868e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
6878e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6888e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)removeItems:(NSArray *)entries
6898e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
6908e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if ([_historyPrivate removeItems:entries]) {
6918e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [self _sendNotification:WebHistoryItemsRemovedNotification
6928e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                        entries:entries];
6938e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
6948e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
6958e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
6968e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)removeAllItems
6978e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
698635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    NSArray *entries = [_historyPrivate allItems];
699635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    if ([_historyPrivate removeAllItems])
700635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        [self _sendNotification:WebHistoryAllItemsRemovedNotification entries:entries];
7018e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
7028e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7038e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)addItems:(NSArray *)newEntries
7048e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
7058e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [_historyPrivate addItems:newEntries];
7068e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [self _sendNotification:WebHistoryItemsAddedNotification
7078e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                    entries:newEntries];
7088e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
7098e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
710cad810f21b803229eb11403f9209855525a25d57Steve Block// MARK: DATE-BASED RETRIEVAL
7118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (NSArray *)orderedLastVisitedDays
7138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
7148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return [_historyPrivate orderedLastVisitedDays];
7158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
7168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (NSArray *)orderedItemsLastVisitedOnDay:(NSCalendarDate *)date
7188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
7198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return [_historyPrivate orderedItemsLastVisitedOnDay:date];
7208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
7218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
722cad810f21b803229eb11403f9209855525a25d57Steve Block// MARK: URL MATCHING
7238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)containsURL:(NSURL *)URL
7258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
7268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return [_historyPrivate containsURL:URL];
7278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
7288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (WebHistoryItem *)itemForURL:(NSURL *)URL
7308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
7318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return [_historyPrivate itemForURL:URL];
7328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
7338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
734cad810f21b803229eb11403f9209855525a25d57Steve Block// MARK: SAVING TO DISK
7358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)loadFromURL:(NSURL *)URL error:(NSError **)error
7378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
7388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    NSMutableArray *discardedItems = [[NSMutableArray alloc] init];
7398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (![_historyPrivate loadFromURL:URL collectDiscardedItemsInto:discardedItems error:error]) {
7408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [discardedItems release];
7418e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return NO;
7428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
7438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [[NSNotificationCenter defaultCenter]
7458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        postNotificationName:WebHistoryLoadedNotification
7468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                      object:self];
7478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if ([discardedItems count])
7498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        [self _sendNotification:WebHistoryItemsDiscardedWhileLoadingNotification entries:discardedItems];
7508e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7518e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [discardedItems release];
7528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return YES;
7538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
7548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7558e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (BOOL)saveToURL:(NSURL *)URL error:(NSError **)error
7568e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
7578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (![_historyPrivate saveToURL:URL error:error])
7588e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return NO;
7598e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [[NSNotificationCenter defaultCenter]
7608e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        postNotificationName:WebHistorySavedNotification
7618e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project                      object:self];
7628e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return YES;
7638e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
7648e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7658e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)setHistoryItemLimit:(int)limit
7668e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
7678e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [_historyPrivate setHistoryItemLimit:limit];
7688e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
7698e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7708e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (int)historyItemLimit
7718e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
7728e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return [_historyPrivate historyItemLimit];
7738e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
7748e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7758e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)setHistoryAgeInDaysLimit:(int)limit
7768e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
7778e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [_historyPrivate setHistoryAgeInDaysLimit:limit];
7788e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
7798e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7808e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (int)historyAgeInDaysLimit
7818e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
7828e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return [_historyPrivate historyAgeInDaysLimit];
7838e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
7848e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7858e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project@end
7868e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7878e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project@implementation WebHistory (WebPrivate)
7888e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
7898e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (WebHistoryItem *)_itemForURLString:(NSString *)URLString
7908e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
7918e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return [_historyPrivate itemForURLString:URLString];
7928e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
7938e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
794635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project- (NSArray *)allItems
795635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
796635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    return [_historyPrivate allItems];
797635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
798635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
7990bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (NSData *)_data
8000bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{
8010bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    return [_historyPrivate data];
8020bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch}
8030bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
804cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block+ (void)_setVisitedLinkTrackingEnabled:(BOOL)visitedLinkTrackingEnabled
805cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block{
806cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    PageGroup::setShouldTrackVisitedLinks(visitedLinkTrackingEnabled);
807cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block}
808cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
809cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block+ (void)_removeAllVisitedLinks
810cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block{
811cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block    PageGroup::removeAllVisitedLinks();
812cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block}
813cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
8148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project@end
8158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
8168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project@implementation WebHistory (WebInternal)
8178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
8180bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (void)_visitedURL:(NSURL *)url withTitle:(NSString *)title method:(NSString *)method wasFailure:(BOOL)wasFailure increaseVisitCount:(BOOL)increaseVisitCount
8198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
8200bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    WebHistoryItem *entry = [_historyPrivate visitedURL:url withTitle:title increaseVisitCount:increaseVisitCount];
821635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
822635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    HistoryItem* item = core(entry);
823635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    item->setLastVisitWasFailure(wasFailure);
824635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
825635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    if ([method length])
826563af33bc48281d19dce701398dbb88cb54fd7ecCary Clark        item->setLastVisitWasHTTPNonGet([method caseInsensitiveCompare:@"GET"] && (![[url scheme] caseInsensitiveCompare:@"http"] || ![[url scheme] caseInsensitiveCompare:@"https"]));
827635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
8280bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    item->setRedirectURLs(0);
829635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
830635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    NSArray *entries = [[NSArray alloc] initWithObjects:entry, nil];
831635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    [self _sendNotification:WebHistoryItemsAddedNotification entries:entries];
832635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    [entries release];
833635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
834635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
8358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project- (void)_addVisitedLinksToPageGroup:(WebCore::PageGroup&)group
8368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
8378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    [_historyPrivate addVisitedLinksToPageGroup:group];
8388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
8398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
8408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project@end
8410bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
8420bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben MurdochWebHistoryWriter::WebHistoryWriter(DateToEntriesMap* entriesByDate)
8430bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    : m_entriesByDate(entriesByDate)
8440bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{
8450bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    m_dateKeys.reserveCapacity(m_entriesByDate->size());
8460bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    DateToEntriesMap::const_iterator end = m_entriesByDate->end();
8470bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    for (DateToEntriesMap::const_iterator it = m_entriesByDate->begin(); it != end; ++it)
8480bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        m_dateKeys.append(it->first);
8490bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    sort(m_dateKeys.begin(), m_dateKeys.end());
8500bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch}
8510bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
8520bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochvoid WebHistoryWriter::writeHistoryItems(BinaryPropertyListObjectStream& stream)
8530bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{
8540bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    for (int dateIndex = m_dateKeys.size() - 1; dateIndex >= 0; dateIndex--) {
8550bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        NSArray *entries = m_entriesByDate->get(m_dateKeys[dateIndex]).get();
8560bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        NSUInteger entryCount = [entries count];
8570bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        for (NSUInteger entryIndex = 0; entryIndex < entryCount; ++entryIndex)
8580bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch            writeHistoryItem(stream, core([entries objectAtIndex:entryIndex]));
8590bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    }
8600bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch}
861