summaryrefslogtreecommitdiffstats
path: root/Source/WebKit/mac/History
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit/mac/History')
-rw-r--r--Source/WebKit/mac/History/WebBackForwardList.h190
-rw-r--r--Source/WebKit/mac/History/WebBackForwardList.mm321
-rw-r--r--Source/WebKit/mac/History/WebBackForwardListInternal.h41
-rw-r--r--Source/WebKit/mac/History/WebBackForwardListPrivate.h42
-rw-r--r--Source/WebKit/mac/History/WebHistory.h165
-rw-r--r--Source/WebKit/mac/History/WebHistory.mm860
-rw-r--r--Source/WebKit/mac/History/WebHistoryInternal.h38
-rw-r--r--Source/WebKit/mac/History/WebHistoryItem.h126
-rw-r--r--Source/WebKit/mac/History/WebHistoryItem.mm659
-rw-r--r--Source/WebKit/mac/History/WebHistoryItemInternal.h61
-rw-r--r--Source/WebKit/mac/History/WebHistoryItemPrivate.h67
-rw-r--r--Source/WebKit/mac/History/WebHistoryPrivate.h58
-rw-r--r--Source/WebKit/mac/History/WebURLsWithTitles.h55
-rw-r--r--Source/WebKit/mac/History/WebURLsWithTitles.m107
14 files changed, 2790 insertions, 0 deletions
diff --git a/Source/WebKit/mac/History/WebBackForwardList.h b/Source/WebKit/mac/History/WebBackForwardList.h
new file mode 100644
index 0000000..9806a64
--- /dev/null
+++ b/Source/WebKit/mac/History/WebBackForwardList.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Foundation/Foundation.h>
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
+#define WebNSUInteger unsigned int
+#else
+#define WebNSUInteger NSUInteger
+#endif
+
+@class WebHistoryItem;
+@class WebBackForwardListPrivate;
+
+/*!
+ @class WebBackForwardList
+ WebBackForwardList holds an ordered list of WebHistoryItems that comprises the back and
+ forward lists.
+
+ Note that the methods which modify instances of this class do not cause
+ navigation to happen in other layers of the stack; they are only for maintaining this data
+ structure.
+*/
+@interface WebBackForwardList : NSObject {
+@private
+ WebBackForwardListPrivate *_private;
+}
+
+/*!
+ @method addItem:
+ @abstract Adds an entry to the list.
+ @param entry The entry to add.
+ @discussion The added entry is inserted immediately after the current entry.
+ If the current position in the list is not at the end of the list, elements in the
+ forward list will be dropped at this point. In addition, entries may be dropped to keep
+ the size of the list within the maximum size.
+*/
+- (void)addItem:(WebHistoryItem *)item;
+
+/*!
+ @method goBack
+ @abstract Move the current pointer back to the entry before the current entry.
+*/
+- (void)goBack;
+
+/*!
+ @method goForward
+ @abstract Move the current pointer ahead to the entry after the current entry.
+*/
+- (void)goForward;
+
+/*!
+ @method goToItem:
+ @abstract Move the current pointer to the given entry.
+ @param item The history item to move the pointer to
+*/
+- (void)goToItem:(WebHistoryItem *)item;
+
+/*!
+ @method backItem
+ @abstract Returns the entry right before the current entry.
+ @result The entry right before the current entry, or nil if there isn't one.
+*/
+- (WebHistoryItem *)backItem;
+
+/*!
+ @method currentItem
+ @abstract Returns the current entry.
+ @result The current entry.
+*/
+- (WebHistoryItem *)currentItem;
+
+/*!
+ @method forwardItem
+ @abstract Returns the entry right after the current entry.
+ @result The entry right after the current entry, or nil if there isn't one.
+*/
+- (WebHistoryItem *)forwardItem;
+
+/*!
+ @method backListWithLimit:
+ @abstract Returns a portion of the list before the current entry.
+ @param limit A cap on the size of the array returned.
+ @result An array of items before the current entry, or nil if there are none. The entries are in the order that they were originally visited.
+*/
+- (NSArray *)backListWithLimit:(int)limit;
+
+/*!
+ @method forwardListWithLimit:
+ @abstract Returns a portion of the list after the current entry.
+ @param limit A cap on the size of the array returned.
+ @result An array of items after the current entry, or nil if there are none. The entries are in the order that they were originally visited.
+*/
+- (NSArray *)forwardListWithLimit:(int)limit;
+
+/*!
+ @method capacity
+ @abstract Returns the list's maximum size.
+ @result The list's maximum size.
+*/
+- (int)capacity;
+
+/*!
+ @method setCapacity
+ @abstract Sets the list's maximum size.
+ @param size The new maximum size for the list.
+*/
+- (void)setCapacity:(int)size;
+
+/*!
+ @method backListCount
+ @abstract Returns the back list's current count.
+ @result The number of items in the list.
+*/
+- (int)backListCount;
+
+/*!
+ @method forwardListCount
+ @abstract Returns the forward list's current count.
+ @result The number of items in the list.
+*/
+- (int)forwardListCount;
+
+/*!
+ @method containsItem:
+ @param item The item that will be checked for presence in the WebBackForwardList.
+ @result Returns YES if the item is in the list.
+*/
+- (BOOL)containsItem:(WebHistoryItem *)item;
+
+/*!
+ @method itemAtIndex:
+ @abstract Returns an entry the given distance from the current entry.
+ @param index Index of the desired list item relative to the current item; 0 is current item, -1 is back item, 1 is forward item, etc.
+ @result The entry the given distance from the current entry. If index exceeds the limits of the list, nil is returned.
+*/
+- (WebHistoryItem *)itemAtIndex:(int)index;
+
+@end
+
+@interface WebBackForwardList(WebBackForwardListDeprecated)
+
+// The following methods are deprecated, and exist for backward compatibility only.
+// Use -[WebPreferences setUsesPageCache] and -[WebPreferences usesPageCache]
+// instead.
+
+/*!
+ @method setPageCacheSize:
+ @abstract The size passed to this method determines whether the WebView
+ associated with this WebBackForwardList will use the shared page cache.
+ @param size If size is 0, the WebView associated with this WebBackForwardList
+ will not use the shared page cache. Otherwise, it will.
+*/
+- (void)setPageCacheSize:(WebNSUInteger)size;
+
+/*!
+ @method pageCacheSize
+ @abstract Returns the size of the shared page cache, or 0.
+ @result The size of the shared page cache (in pages), or 0 if the WebView
+ associated with this WebBackForwardList will not use the shared page cache.
+*/
+- (WebNSUInteger)pageCacheSize;
+@end
+
+#undef WebNSUInteger
diff --git a/Source/WebKit/mac/History/WebBackForwardList.mm b/Source/WebKit/mac/History/WebBackForwardList.mm
new file mode 100644
index 0000000..e794c2f
--- /dev/null
+++ b/Source/WebKit/mac/History/WebBackForwardList.mm
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2005, 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "WebBackForwardList.h"
+#import "WebBackForwardListInternal.h"
+
+#import "WebFrameInternal.h"
+#import "WebHistoryItemInternal.h"
+#import "WebHistoryItemPrivate.h"
+#import "WebKitLogging.h"
+#import "WebKitVersionChecks.h"
+#import "WebNSObjectExtras.h"
+#import "WebPreferencesPrivate.h"
+#import "WebTypesInternal.h"
+#import "WebViewPrivate.h"
+#import <WebCore/BackForwardListImpl.h>
+#import <WebCore/HistoryItem.h>
+#import <WebCore/Page.h>
+#import <WebCore/PageCache.h>
+#import <WebCore/Settings.h>
+#import <WebCore/ThreadCheck.h>
+#import <WebCore/WebCoreObjCExtras.h>
+#import <runtime/InitializeThreading.h>
+#import <wtf/Assertions.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/StdLibExtras.h>
+#import <wtf/Threading.h>
+
+using namespace WebCore;
+
+typedef HashMap<BackForwardListImpl*, WebBackForwardList*> BackForwardListMap;
+
+// FIXME: Instead of this we could just create a class derived from BackForwardListImpl
+// with a pointer to a WebBackForwardList in it.
+static BackForwardListMap& backForwardLists()
+{
+ DEFINE_STATIC_LOCAL(BackForwardListMap, staticBackForwardLists, ());
+ return staticBackForwardLists;
+}
+
+@implementation WebBackForwardList (WebBackForwardListInternal)
+
+BackForwardListImpl* core(WebBackForwardList *webBackForwardList)
+{
+ if (!webBackForwardList)
+ return 0;
+
+ return reinterpret_cast<BackForwardListImpl*>(webBackForwardList->_private);
+}
+
+WebBackForwardList *kit(BackForwardListImpl* backForwardList)
+{
+ if (!backForwardList)
+ return nil;
+
+ if (WebBackForwardList *webBackForwardList = backForwardLists().get(backForwardList))
+ return webBackForwardList;
+
+ return [[[WebBackForwardList alloc] initWithBackForwardList:backForwardList] autorelease];
+}
+
+- (id)initWithBackForwardList:(PassRefPtr<BackForwardListImpl>)backForwardList
+{
+ WebCoreThreadViolationCheckRoundOne();
+ self = [super init];
+ if (!self)
+ return nil;
+
+ _private = reinterpret_cast<WebBackForwardListPrivate*>(backForwardList.releaseRef());
+ backForwardLists().set(core(self), self);
+ return self;
+}
+
+@end
+
+@implementation WebBackForwardList
+
++ (void)initialize
+{
+ JSC::initializeThreading();
+ WTF::initializeMainThreadToProcessMainThread();
+#ifndef BUILDING_ON_TIGER
+ WebCoreObjCFinalizeOnMainThread(self);
+#endif
+}
+
+- (id)init
+{
+ return [self initWithBackForwardList:BackForwardListImpl::create(0)];
+}
+
+- (void)dealloc
+{
+ if (WebCoreObjCScheduleDeallocateOnMainThread([WebBackForwardList class], self))
+ return;
+
+ BackForwardListImpl* backForwardList = core(self);
+ ASSERT(backForwardList);
+ if (backForwardList) {
+ ASSERT(backForwardList->closed());
+ backForwardLists().remove(backForwardList);
+ backForwardList->deref();
+ }
+
+ [super dealloc];
+}
+
+- (void)finalize
+{
+ WebCoreThreadViolationCheckRoundOne();
+ BackForwardListImpl* backForwardList = core(self);
+ ASSERT(backForwardList);
+ if (backForwardList) {
+ ASSERT(backForwardList->closed());
+ backForwardLists().remove(backForwardList);
+ backForwardList->deref();
+ }
+
+ [super finalize];
+}
+
+- (void)_close
+{
+ core(self)->close();
+}
+
+- (void)addItem:(WebHistoryItem *)entry
+{
+ core(self)->addItem(core(entry));
+
+ // Since the assumed contract with WebBackForwardList is that it retains its WebHistoryItems,
+ // the following line prevents a whole class of problems where a history item will be created in
+ // a function, added to the BFlist, then used in the rest of that function.
+ [[entry retain] autorelease];
+}
+
+- (void)removeItem:(WebHistoryItem *)item
+{
+ core(self)->removeItem(core(item));
+}
+
+- (BOOL)containsItem:(WebHistoryItem *)item
+{
+ return core(self)->containsItem(core(item));
+}
+
+- (void)goBack
+{
+ core(self)->goBack();
+}
+
+- (void)goForward
+{
+ core(self)->goForward();
+}
+
+- (void)goToItem:(WebHistoryItem *)item
+{
+ core(self)->goToItem(core(item));
+}
+
+- (WebHistoryItem *)backItem
+{
+ return [[kit(core(self)->backItem()) retain] autorelease];
+}
+
+- (WebHistoryItem *)currentItem
+{
+ return [[kit(core(self)->currentItem()) retain] autorelease];
+}
+
+- (WebHistoryItem *)forwardItem
+{
+ return [[kit(core(self)->forwardItem()) retain] autorelease];
+}
+
+static NSArray* vectorToNSArray(HistoryItemVector& list)
+{
+ unsigned size = list.size();
+ NSMutableArray *result = [[[NSMutableArray alloc] initWithCapacity:size] autorelease];
+ for (unsigned i = 0; i < size; ++i)
+ [result addObject:kit(list[i].get())];
+
+ return result;
+}
+
+static bool bumperCarBackForwardHackNeeded()
+{
+ static bool hackNeeded = [[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.freeverse.bumpercar"] &&
+ !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_BUMPERCAR_BACK_FORWARD_QUIRK);
+
+ return hackNeeded;
+}
+
+- (NSArray *)backListWithLimit:(int)limit
+{
+ HistoryItemVector list;
+ core(self)->backListWithLimit(limit, list);
+ NSArray *result = vectorToNSArray(list);
+
+ if (bumperCarBackForwardHackNeeded()) {
+ static NSArray *lastBackListArray = nil;
+ [lastBackListArray release];
+ lastBackListArray = [result retain];
+ }
+
+ return result;
+}
+
+- (NSArray *)forwardListWithLimit:(int)limit
+{
+ HistoryItemVector list;
+ core(self)->forwardListWithLimit(limit, list);
+ NSArray *result = vectorToNSArray(list);
+
+ if (bumperCarBackForwardHackNeeded()) {
+ static NSArray *lastForwardListArray = nil;
+ [lastForwardListArray release];
+ lastForwardListArray = [result retain];
+ }
+
+ return result;
+}
+
+- (int)capacity
+{
+ return core(self)->capacity();
+}
+
+- (void)setCapacity:(int)size
+{
+ core(self)->setCapacity(size);
+}
+
+
+-(NSString *)description
+{
+ NSMutableString *result;
+
+ result = [NSMutableString stringWithCapacity:512];
+
+ [result appendString:@"\n--------------------------------------------\n"];
+ [result appendString:@"WebBackForwardList:\n"];
+
+ BackForwardListImpl* backForwardList = core(self);
+ HistoryItemVector& entries = backForwardList->entries();
+
+ unsigned size = entries.size();
+ for (unsigned i = 0; i < size; ++i) {
+ if (entries[i] == backForwardList->currentItem()) {
+ [result appendString:@" >>>"];
+ } else {
+ [result appendString:@" "];
+ }
+ [result appendFormat:@"%2d) ", i];
+ int currPos = [result length];
+ [result appendString:[kit(entries[i].get()) description]];
+
+ // shift all the contents over. a bit slow, but this is for debugging
+ NSRange replRange = {currPos, [result length]-currPos};
+ [result replaceOccurrencesOfString:@"\n" withString:@"\n " options:0 range:replRange];
+
+ [result appendString:@"\n"];
+ }
+
+ [result appendString:@"\n--------------------------------------------\n"];
+
+ return result;
+}
+
+- (void)setPageCacheSize:(NSUInteger)size
+{
+ [kit(core(self)->page()) setUsesPageCache:size != 0];
+}
+
+- (NSUInteger)pageCacheSize
+{
+ return [kit(core(self)->page()) usesPageCache] ? pageCache()->capacity() : 0;
+}
+
+- (int)backListCount
+{
+ return core(self)->backListCount();
+}
+
+- (int)forwardListCount
+{
+ return core(self)->forwardListCount();
+}
+
+- (WebHistoryItem *)itemAtIndex:(int)index
+{
+ return [[kit(core(self)->itemAtIndex(index)) retain] autorelease];
+}
+
+@end
diff --git a/Source/WebKit/mac/History/WebBackForwardListInternal.h b/Source/WebKit/mac/History/WebBackForwardListInternal.h
new file mode 100644
index 0000000..1afb833
--- /dev/null
+++ b/Source/WebKit/mac/History/WebBackForwardListInternal.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <WebKit/WebBackForwardList.h>
+#import <wtf/PassRefPtr.h>
+
+namespace WebCore {
+ class BackForwardListImpl;
+}
+
+WebCore::BackForwardListImpl* core(WebBackForwardList *);
+WebBackForwardList *kit(WebCore::BackForwardListImpl*);
+
+@interface WebBackForwardList (WebBackForwardListInternal)
+- (id)initWithBackForwardList:(PassRefPtr<WebCore::BackForwardListImpl>)backForwardList;
+@end
diff --git a/Source/WebKit/mac/History/WebBackForwardListPrivate.h b/Source/WebKit/mac/History/WebBackForwardListPrivate.h
new file mode 100644
index 0000000..7e76e9b
--- /dev/null
+++ b/Source/WebKit/mac/History/WebBackForwardListPrivate.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#import <WebKit/WebBackForwardList.h>
+
+@interface WebBackForwardList (WebBackForwardListPrivate)
+
+/*!
+ @method removeItem:
+ @abstract Removes an entry from the list.
+ @param entry The entry to remove. Cannot be the current item.
+*/
+- (void)removeItem:(WebHistoryItem *)item;
+
+@end
diff --git a/Source/WebKit/mac/History/WebHistory.h b/Source/WebKit/mac/History/WebHistory.h
new file mode 100644
index 0000000..8a6ec0b
--- /dev/null
+++ b/Source/WebKit/mac/History/WebHistory.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2003, 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Foundation/Foundation.h>
+
+@class NSError;
+
+@class WebHistoryItem;
+@class WebHistoryPrivate;
+
+/*
+ @discussion Notifications sent when history is modified.
+ @constant WebHistoryItemsAddedNotification Posted from addItems:. This
+ notification comes with a userInfo dictionary that contains the array of
+ items added. The key for the array is WebHistoryItemsKey.
+ @constant WebHistoryItemsRemovedNotification Posted from removeItems:.
+ This notification comes with a userInfo dictionary that contains the array of
+ items removed. The key for the array is WebHistoryItemsKey.
+ @constant WebHistoryAllItemsRemovedNotification Posted from removeAllItems
+ @constant WebHistoryLoadedNotification Posted from loadFromURL:error:.
+*/
+extern NSString *WebHistoryItemsAddedNotification;
+extern NSString *WebHistoryItemsRemovedNotification;
+extern NSString *WebHistoryAllItemsRemovedNotification;
+extern NSString *WebHistoryLoadedNotification;
+extern NSString *WebHistorySavedNotification;
+
+extern NSString *WebHistoryItemsKey;
+
+/*!
+ @class WebHistory
+ @discussion WebHistory is used to track pages that have been loaded
+ by WebKit.
+*/
+@interface WebHistory : NSObject {
+@private
+ WebHistoryPrivate *_historyPrivate;
+}
+
+/*!
+ @method optionalSharedHistory
+ @abstract Returns a shared WebHistory instance initialized with the default history file.
+ @result A WebHistory object.
+*/
++ (WebHistory *)optionalSharedHistory;
+
+/*!
+ @method setOptionalSharedHistory:
+ @param history The history to use for the global WebHistory.
+*/
++ (void)setOptionalSharedHistory:(WebHistory *)history;
+
+/*!
+ @method loadFromURL:error:
+ @param URL The URL to use to initialize the WebHistory.
+ @param error Set to nil or an NSError instance if an error occurred.
+ @abstract The designated initializer for WebHistory.
+ @result Returns YES if successful, NO otherwise.
+*/
+- (BOOL)loadFromURL:(NSURL *)URL error:(NSError **)error;
+
+/*!
+ @method saveToURL:error:
+ @discussion Save history to URL. It is the client's responsibility to call this at appropriate times.
+ @param URL The URL to use to save the WebHistory.
+ @param error Set to nil or an NSError instance if an error occurred.
+ @result Returns YES if successful, NO otherwise.
+*/
+- (BOOL)saveToURL:(NSURL *)URL error:(NSError **)error;
+
+/*!
+ @method addItems:
+ @param newItems An array of WebHistoryItems to add to the WebHistory.
+*/
+- (void)addItems:(NSArray *)newItems;
+
+/*!
+ @method removeItems:
+ @param items An array of WebHistoryItems to remove from the WebHistory.
+*/
+- (void)removeItems:(NSArray *)items;
+
+/*!
+ @method removeAllItems
+*/
+- (void)removeAllItems;
+
+/*!
+ @method orderedLastVisitedDays
+ @discussion Get an array of NSCalendarDates, each one representing a unique day that contains one
+ or more history items, ordered from most recent to oldest.
+ @result Returns an array of NSCalendarDates for which history items exist in the WebHistory.
+*/
+- (NSArray *)orderedLastVisitedDays;
+
+/*!
+ @method orderedItemsLastVisitedOnDay:
+ @discussion Get an array of WebHistoryItem that were last visited on the day represented by the
+ specified NSCalendarDate, ordered from most recent to oldest.
+ @param calendarDate A date identifying the unique day of interest.
+ @result Returns an array of WebHistoryItems last visited on the indicated day.
+*/
+- (NSArray *)orderedItemsLastVisitedOnDay:(NSCalendarDate *)calendarDate;
+
+/*!
+ @method itemForURL:
+ @abstract Get an item for a specific URL
+ @param URL The URL of the history item to search for
+ @result Returns an item matching the URL
+*/
+- (WebHistoryItem *)itemForURL:(NSURL *)URL;
+
+/*!
+ @method setHistoryItemLimit:
+ @discussion Limits the number of items that will be stored by the WebHistory.
+ @param limit The maximum number of items that will be stored by the WebHistory.
+*/
+- (void)setHistoryItemLimit:(int)limit;
+
+/*!
+ @method historyItemLimit
+ @result The maximum number of items that will be stored by the WebHistory.
+*/
+- (int)historyItemLimit;
+
+/*!
+ @method setHistoryAgeInDaysLimit:
+ @discussion setHistoryAgeInDaysLimit: sets the maximum number of days to be read from
+ stored history.
+ @param limit The maximum number of days to be read from stored history.
+*/
+- (void)setHistoryAgeInDaysLimit:(int)limit;
+
+/*!
+ @method historyAgeInDaysLimit
+ @return Returns the maximum number of days to be read from stored history.
+*/
+- (int)historyAgeInDaysLimit;
+
+@end
diff --git a/Source/WebKit/mac/History/WebHistory.mm b/Source/WebKit/mac/History/WebHistory.mm
new file mode 100644
index 0000000..00cee78
--- /dev/null
+++ b/Source/WebKit/mac/History/WebHistory.mm
@@ -0,0 +1,860 @@
+/*
+ * Copyright (C) 2005, 2008, 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "WebHistoryInternal.h"
+
+#import "WebHistoryItemInternal.h"
+#import "WebKitLogging.h"
+#import "WebNSURLExtras.h"
+#import "WebTypesInternal.h"
+#import <WebCore/HistoryItem.h>
+#import <WebCore/HistoryPropertyList.h>
+#import <WebCore/PageGroup.h>
+
+using namespace WebCore;
+using namespace std;
+
+typedef int64_t WebHistoryDateKey;
+typedef HashMap<WebHistoryDateKey, RetainPtr<NSMutableArray> > DateToEntriesMap;
+
+NSString *WebHistoryItemsAddedNotification = @"WebHistoryItemsAddedNotification";
+NSString *WebHistoryItemsRemovedNotification = @"WebHistoryItemsRemovedNotification";
+NSString *WebHistoryAllItemsRemovedNotification = @"WebHistoryAllItemsRemovedNotification";
+NSString *WebHistoryLoadedNotification = @"WebHistoryLoadedNotification";
+NSString *WebHistoryItemsDiscardedWhileLoadingNotification = @"WebHistoryItemsDiscardedWhileLoadingNotification";
+NSString *WebHistorySavedNotification = @"WebHistorySavedNotification";
+NSString *WebHistoryItemsKey = @"WebHistoryItems";
+
+static WebHistory *_sharedHistory = nil;
+
+NSString *FileVersionKey = @"WebHistoryFileVersion";
+NSString *DatesArrayKey = @"WebHistoryDates";
+
+#define currentFileVersion 1
+
+class WebHistoryWriter : public HistoryPropertyListWriter {
+public:
+ WebHistoryWriter(DateToEntriesMap*);
+
+private:
+ virtual void writeHistoryItems(BinaryPropertyListObjectStream&);
+
+ DateToEntriesMap* m_entriesByDate;
+ Vector<int> m_dateKeys;
+};
+
+@interface WebHistoryPrivate : NSObject {
+@private
+ NSMutableDictionary *_entriesByURL;
+ DateToEntriesMap* _entriesByDate;
+ NSMutableArray *_orderedLastVisitedDays;
+ BOOL itemLimitSet;
+ int itemLimit;
+ BOOL ageInDaysLimitSet;
+ int ageInDaysLimit;
+}
+
+- (WebHistoryItem *)visitedURL:(NSURL *)url withTitle:(NSString *)title increaseVisitCount:(BOOL)increaseVisitCount;
+
+- (BOOL)addItem:(WebHistoryItem *)entry discardDuplicate:(BOOL)discardDuplicate;
+- (void)addItems:(NSArray *)newEntries;
+- (BOOL)removeItem:(WebHistoryItem *)entry;
+- (BOOL)removeItems:(NSArray *)entries;
+- (BOOL)removeAllItems;
+
+- (NSArray *)orderedLastVisitedDays;
+- (NSArray *)orderedItemsLastVisitedOnDay:(NSCalendarDate *)calendarDate;
+- (BOOL)containsURL:(NSURL *)URL;
+- (WebHistoryItem *)itemForURL:(NSURL *)URL;
+- (WebHistoryItem *)itemForURLString:(NSString *)URLString;
+- (NSArray *)allItems;
+
+- (BOOL)loadFromURL:(NSURL *)URL collectDiscardedItemsInto:(NSMutableArray *)discardedItems error:(NSError **)error;
+- (BOOL)saveToURL:(NSURL *)URL error:(NSError **)error;
+
+- (NSCalendarDate *)ageLimitDate;
+
+- (void)setHistoryItemLimit:(int)limit;
+- (int)historyItemLimit;
+- (void)setHistoryAgeInDaysLimit:(int)limit;
+- (int)historyAgeInDaysLimit;
+
+- (void)addVisitedLinksToPageGroup:(PageGroup&)group;
+
+@end
+
+@implementation WebHistoryPrivate
+
+// MARK: OBJECT FRAMEWORK
+
++ (void)initialize
+{
+ [[NSUserDefaults standardUserDefaults] registerDefaults:
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ @"1000", @"WebKitHistoryItemLimit",
+ @"7", @"WebKitHistoryAgeInDaysLimit",
+ nil]];
+}
+
+- (id)init
+{
+ if (![super init])
+ return nil;
+
+ _entriesByURL = [[NSMutableDictionary alloc] init];
+ _entriesByDate = new DateToEntriesMap;
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_entriesByURL release];
+ [_orderedLastVisitedDays release];
+ delete _entriesByDate;
+ [super dealloc];
+}
+
+- (void)finalize
+{
+ delete _entriesByDate;
+ [super finalize];
+}
+
+// MARK: MODIFYING CONTENTS
+
+static void getDayBoundaries(NSTimeInterval interval, NSTimeInterval& beginningOfDay, NSTimeInterval& beginningOfNextDay)
+{
+ CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
+ CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(interval, timeZone);
+ date.hour = 0;
+ date.minute = 0;
+ date.second = 0;
+ beginningOfDay = CFGregorianDateGetAbsoluteTime(date, timeZone);
+ date.day += 1;
+ beginningOfNextDay = CFGregorianDateGetAbsoluteTime(date, timeZone);
+ CFRelease(timeZone);
+}
+
+static inline NSTimeInterval beginningOfDay(NSTimeInterval date)
+{
+ static NSTimeInterval cachedBeginningOfDay = NAN;
+ static NSTimeInterval cachedBeginningOfNextDay;
+ if (!(date >= cachedBeginningOfDay && date < cachedBeginningOfNextDay))
+ getDayBoundaries(date, cachedBeginningOfDay, cachedBeginningOfNextDay);
+ return cachedBeginningOfDay;
+}
+
+static inline WebHistoryDateKey dateKey(NSTimeInterval date)
+{
+ // Converting from double (NSTimeInterval) to int64_t (WebHistoryDateKey) is
+ // safe here because all sensible dates are in the range -2**48 .. 2**47 which
+ // safely fits in an int64_t.
+ return beginningOfDay(date);
+}
+
+// Returns whether the day is already in the list of days,
+// and fills in *key with the key used to access its location
+- (BOOL)findKey:(WebHistoryDateKey*)key forDay:(NSTimeInterval)date
+{
+ ASSERT_ARG(key, key);
+ *key = dateKey(date);
+ return _entriesByDate->contains(*key);
+}
+
+- (void)insertItem:(WebHistoryItem *)entry forDateKey:(WebHistoryDateKey)dateKey
+{
+ ASSERT_ARG(entry, entry != nil);
+ ASSERT(_entriesByDate->contains(dateKey));
+
+ NSMutableArray *entriesForDate = _entriesByDate->get(dateKey).get();
+ NSTimeInterval entryDate = [entry lastVisitedTimeInterval];
+
+ unsigned count = [entriesForDate count];
+
+ // The entries for each day are stored in a sorted array with the most recent entry first
+ // Check for the common cases of the entry being newer than all existing entries or the first entry of the day
+ if (!count || [[entriesForDate objectAtIndex:0] lastVisitedTimeInterval] < entryDate) {
+ [entriesForDate insertObject:entry atIndex:0];
+ return;
+ }
+ // .. or older than all existing entries
+ if (count > 0 && [[entriesForDate objectAtIndex:count - 1] lastVisitedTimeInterval] >= entryDate) {
+ [entriesForDate insertObject:entry atIndex:count];
+ return;
+ }
+
+ unsigned low = 0;
+ unsigned high = count;
+ while (low < high) {
+ unsigned mid = low + (high - low) / 2;
+ if ([[entriesForDate objectAtIndex:mid] lastVisitedTimeInterval] >= entryDate)
+ low = mid + 1;
+ else
+ high = mid;
+ }
+
+ // low is now the index of the first entry that is older than entryDate
+ [entriesForDate insertObject:entry atIndex:low];
+}
+
+- (BOOL)removeItemFromDateCaches:(WebHistoryItem *)entry
+{
+ WebHistoryDateKey dateKey;
+ BOOL foundDate = [self findKey:&dateKey forDay:[entry lastVisitedTimeInterval]];
+
+ if (!foundDate)
+ return NO;
+
+ DateToEntriesMap::iterator it = _entriesByDate->find(dateKey);
+ NSMutableArray *entriesForDate = it->second.get();
+ [entriesForDate removeObjectIdenticalTo:entry];
+
+ // remove this date entirely if there are no other entries on it
+ if ([entriesForDate count] == 0) {
+ _entriesByDate->remove(it);
+ // Clear _orderedLastVisitedDays so it will be regenerated when next requested.
+ [_orderedLastVisitedDays release];
+ _orderedLastVisitedDays = nil;
+ }
+
+ return YES;
+}
+
+- (BOOL)removeItemForURLString:(NSString *)URLString
+{
+ WebHistoryItem *entry = [_entriesByURL objectForKey:URLString];
+ if (!entry)
+ return NO;
+
+ [_entriesByURL removeObjectForKey:URLString];
+
+#if ASSERT_DISABLED
+ [self removeItemFromDateCaches:entry];
+#else
+ BOOL itemWasInDateCaches = [self removeItemFromDateCaches:entry];
+ ASSERT(itemWasInDateCaches);
+#endif
+
+ if (![_entriesByURL count])
+ PageGroup::removeAllVisitedLinks();
+
+ return YES;
+}
+
+- (void)addItemToDateCaches:(WebHistoryItem *)entry
+{
+ WebHistoryDateKey dateKey;
+ if ([self findKey:&dateKey forDay:[entry lastVisitedTimeInterval]])
+ // other entries already exist for this date
+ [self insertItem:entry forDateKey:dateKey];
+ else {
+ // no other entries exist for this date
+ NSMutableArray *entries = [[NSMutableArray alloc] initWithObjects:&entry count:1];
+ _entriesByDate->set(dateKey, entries);
+ [entries release];
+ // Clear _orderedLastVisitedDays so it will be regenerated when next requested.
+ [_orderedLastVisitedDays release];
+ _orderedLastVisitedDays = nil;
+ }
+}
+
+- (WebHistoryItem *)visitedURL:(NSURL *)url withTitle:(NSString *)title increaseVisitCount:(BOOL)increaseVisitCount
+{
+ ASSERT(url);
+ ASSERT(title);
+
+ NSString *URLString = [url _web_originalDataAsString];
+ WebHistoryItem *entry = [_entriesByURL objectForKey:URLString];
+
+ if (entry) {
+ LOG(History, "Updating global history entry %@", entry);
+ // Remove the item from date caches before changing its last visited date. Otherwise we might get duplicate entries
+ // as seen in <rdar://problem/6570573>.
+ BOOL itemWasInDateCaches = [self removeItemFromDateCaches:entry];
+ ASSERT_UNUSED(itemWasInDateCaches, itemWasInDateCaches);
+
+ [entry _visitedWithTitle:title increaseVisitCount:increaseVisitCount];
+ } else {
+ LOG(History, "Adding new global history entry for %@", url);
+ entry = [[WebHistoryItem alloc] initWithURLString:URLString title:title lastVisitedTimeInterval:[NSDate timeIntervalSinceReferenceDate]];
+ [entry _recordInitialVisit];
+ [_entriesByURL setObject:entry forKey:URLString];
+ [entry release];
+ }
+
+ [self addItemToDateCaches:entry];
+
+ return entry;
+}
+
+- (BOOL)addItem:(WebHistoryItem *)entry discardDuplicate:(BOOL)discardDuplicate
+{
+ ASSERT_ARG(entry, entry);
+ ASSERT_ARG(entry, [entry lastVisitedTimeInterval] != 0);
+
+ NSString *URLString = [entry URLString];
+
+ WebHistoryItem *oldEntry = [_entriesByURL objectForKey:URLString];
+ if (oldEntry) {
+ if (discardDuplicate)
+ return NO;
+
+ // The last reference to oldEntry might be this dictionary, so we hold onto a reference
+ // until we're done with oldEntry.
+ [oldEntry retain];
+ [self removeItemForURLString:URLString];
+
+ // If we already have an item with this URL, we need to merge info that drives the
+ // URL autocomplete heuristics from that item into the new one.
+ [entry _mergeAutoCompleteHints:oldEntry];
+ [oldEntry release];
+ }
+
+ [self addItemToDateCaches:entry];
+ [_entriesByURL setObject:entry forKey:URLString];
+
+ return YES;
+}
+
+- (BOOL)removeItem:(WebHistoryItem *)entry
+{
+ NSString *URLString = [entry URLString];
+
+ // If this exact object isn't stored, then make no change.
+ // FIXME: Is this the right behavior if this entry isn't present, but another entry for the same URL is?
+ // Maybe need to change the API to make something like removeEntryForURLString public instead.
+ WebHistoryItem *matchingEntry = [_entriesByURL objectForKey:URLString];
+ if (matchingEntry != entry)
+ return NO;
+
+ [self removeItemForURLString:URLString];
+
+ return YES;
+}
+
+- (BOOL)removeItems:(NSArray *)entries
+{
+ NSUInteger count = [entries count];
+ if (!count)
+ return NO;
+
+ for (NSUInteger index = 0; index < count; ++index)
+ [self removeItem:[entries objectAtIndex:index]];
+
+ return YES;
+}
+
+- (BOOL)removeAllItems
+{
+ if (_entriesByDate->isEmpty())
+ return NO;
+
+ _entriesByDate->clear();
+ [_entriesByURL removeAllObjects];
+
+ // Clear _orderedLastVisitedDays so it will be regenerated when next requested.
+ [_orderedLastVisitedDays release];
+ _orderedLastVisitedDays = nil;
+
+ PageGroup::removeAllVisitedLinks();
+
+ return YES;
+}
+
+- (void)addItems:(NSArray *)newEntries
+{
+ // There is no guarantee that the incoming entries are in any particular
+ // order, but if this is called with a set of entries that were created by
+ // iterating through the results of orderedLastVisitedDays and orderedItemsLastVisitedOnDayy
+ // then they will be ordered chronologically from newest to oldest. We can make adding them
+ // faster (fewer compares) by inserting them from oldest to newest.
+ NSEnumerator *enumerator = [newEntries reverseObjectEnumerator];
+ while (WebHistoryItem *entry = [enumerator nextObject])
+ [self addItem:entry discardDuplicate:NO];
+}
+
+// MARK: DATE-BASED RETRIEVAL
+
+- (NSArray *)orderedLastVisitedDays
+{
+ if (!_orderedLastVisitedDays) {
+ Vector<int> daysAsTimeIntervals;
+ daysAsTimeIntervals.reserveCapacity(_entriesByDate->size());
+ DateToEntriesMap::const_iterator end = _entriesByDate->end();
+ for (DateToEntriesMap::const_iterator it = _entriesByDate->begin(); it != end; ++it)
+ daysAsTimeIntervals.append(it->first);
+
+ sort(daysAsTimeIntervals.begin(), daysAsTimeIntervals.end());
+ size_t count = daysAsTimeIntervals.size();
+ _orderedLastVisitedDays = [[NSMutableArray alloc] initWithCapacity:count];
+ for (int i = count - 1; i >= 0; i--) {
+ NSTimeInterval interval = daysAsTimeIntervals[i];
+ NSCalendarDate *date = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:interval];
+ [_orderedLastVisitedDays addObject:date];
+ [date release];
+ }
+ }
+ return _orderedLastVisitedDays;
+}
+
+- (NSArray *)orderedItemsLastVisitedOnDay:(NSCalendarDate *)date
+{
+ WebHistoryDateKey dateKey;
+ if (![self findKey:&dateKey forDay:[date timeIntervalSinceReferenceDate]])
+ return nil;
+ return _entriesByDate->get(dateKey).get();
+}
+
+// MARK: URL MATCHING
+
+- (WebHistoryItem *)itemForURLString:(NSString *)URLString
+{
+ return [_entriesByURL objectForKey:URLString];
+}
+
+- (BOOL)containsURL:(NSURL *)URL
+{
+ return [self itemForURLString:[URL _web_originalDataAsString]] != nil;
+}
+
+- (WebHistoryItem *)itemForURL:(NSURL *)URL
+{
+ return [self itemForURLString:[URL _web_originalDataAsString]];
+}
+
+- (NSArray *)allItems
+{
+ return [_entriesByURL allValues];
+}
+
+// MARK: ARCHIVING/UNARCHIVING
+
+- (void)setHistoryAgeInDaysLimit:(int)limit
+{
+ ageInDaysLimitSet = YES;
+ ageInDaysLimit = limit;
+}
+
+- (int)historyAgeInDaysLimit
+{
+ if (ageInDaysLimitSet)
+ return ageInDaysLimit;
+ return [[NSUserDefaults standardUserDefaults] integerForKey:@"WebKitHistoryAgeInDaysLimit"];
+}
+
+- (void)setHistoryItemLimit:(int)limit
+{
+ itemLimitSet = YES;
+ itemLimit = limit;
+}
+
+- (int)historyItemLimit
+{
+ if (itemLimitSet)
+ return itemLimit;
+ return [[NSUserDefaults standardUserDefaults] integerForKey:@"WebKitHistoryItemLimit"];
+}
+
+// Return a date that marks the age limit for history entries saved to or
+// loaded from disk. Any entry older than this item should be rejected.
+- (NSCalendarDate *)ageLimitDate
+{
+ return [[NSCalendarDate calendarDate] dateByAddingYears:0 months:0 days:-[self historyAgeInDaysLimit]
+ hours:0 minutes:0 seconds:0];
+}
+
+- (BOOL)loadHistoryGutsFromURL:(NSURL *)URL savedItemsCount:(int *)numberOfItemsLoaded collectDiscardedItemsInto:(NSMutableArray *)discardedItems error:(NSError **)error
+{
+ *numberOfItemsLoaded = 0;
+ NSDictionary *dictionary = nil;
+
+ // Optimize loading from local file, which is faster than using the general URL loading mechanism
+ if ([URL isFileURL]) {
+ dictionary = [NSDictionary dictionaryWithContentsOfFile:[URL path]];
+ if (!dictionary) {
+#if !LOG_DISABLED
+ if ([[NSFileManager defaultManager] fileExistsAtPath:[URL path]])
+ LOG_ERROR("unable to read history from file %@; perhaps contents are corrupted", [URL path]);
+#endif
+ // else file doesn't exist, which is normal the first time
+ return NO;
+ }
+ } else {
+ NSData *data = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:URL] returningResponse:nil error:error];
+ if (data && [data length] > 0) {
+ dictionary = [NSPropertyListSerialization propertyListFromData:data
+ mutabilityOption:NSPropertyListImmutable
+ format:nil
+ errorDescription:nil];
+ }
+ }
+
+ // We used to support NSArrays here, but that was before Safari 1.0 shipped. We will no longer support
+ // that ancient format, so anything that isn't an NSDictionary is bogus.
+ if (![dictionary isKindOfClass:[NSDictionary class]])
+ return NO;
+
+ NSNumber *fileVersionObject = [dictionary objectForKey:FileVersionKey];
+ int fileVersion;
+ // we don't trust data obtained from elsewhere, so double-check
+ if (!fileVersionObject || ![fileVersionObject isKindOfClass:[NSNumber class]]) {
+ LOG_ERROR("history file version can't be determined, therefore not loading");
+ return NO;
+ }
+ fileVersion = [fileVersionObject intValue];
+ if (fileVersion > currentFileVersion) {
+ LOG_ERROR("history file version is %d, newer than newest known version %d, therefore not loading", fileVersion, currentFileVersion);
+ return NO;
+ }
+
+ NSArray *array = [dictionary objectForKey:DatesArrayKey];
+
+ int itemCountLimit = [self historyItemLimit];
+ NSTimeInterval ageLimitDate = [[self ageLimitDate] timeIntervalSinceReferenceDate];
+ NSEnumerator *enumerator = [array objectEnumerator];
+ BOOL ageLimitPassed = NO;
+ BOOL itemLimitPassed = NO;
+ ASSERT(*numberOfItemsLoaded == 0);
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSDictionary *itemAsDictionary;
+ while ((itemAsDictionary = [enumerator nextObject]) != nil) {
+ WebHistoryItem *item = [[WebHistoryItem alloc] initFromDictionaryRepresentation:itemAsDictionary];
+
+ // item without URL is useless; data on disk must have been bad; ignore
+ if ([item URLString]) {
+ // Test against date limit. Since the items are ordered newest to oldest, we can stop comparing
+ // once we've found the first item that's too old.
+ if (!ageLimitPassed && [item lastVisitedTimeInterval] <= ageLimitDate)
+ ageLimitPassed = YES;
+
+ if (ageLimitPassed || itemLimitPassed)
+ [discardedItems addObject:item];
+ else {
+ if ([self addItem:item discardDuplicate:YES])
+ ++(*numberOfItemsLoaded);
+ if (*numberOfItemsLoaded == itemCountLimit)
+ itemLimitPassed = YES;
+
+ // Draining the autorelease pool every 50 iterations was found by experimentation to be optimal
+ if (*numberOfItemsLoaded % 50 == 0) {
+ [pool drain];
+ pool = [[NSAutoreleasePool alloc] init];
+ }
+ }
+ }
+ [item release];
+ }
+ [pool drain];
+
+ return YES;
+}
+
+- (BOOL)loadFromURL:(NSURL *)URL collectDiscardedItemsInto:(NSMutableArray *)discardedItems error:(NSError **)error
+{
+#if !LOG_DISABLED
+ double start = CFAbsoluteTimeGetCurrent();
+#endif
+
+ int numberOfItems;
+ if (![self loadHistoryGutsFromURL:URL savedItemsCount:&numberOfItems collectDiscardedItemsInto:discardedItems error:error])
+ return NO;
+
+#if !LOG_DISABLED
+ double duration = CFAbsoluteTimeGetCurrent() - start;
+ LOG(Timing, "loading %d history entries from %@ took %f seconds", numberOfItems, URL, duration);
+#endif
+
+ return YES;
+}
+
+- (NSData *)data
+{
+ if (_entriesByDate->isEmpty()) {
+ static NSData *emptyHistoryData = (NSData *)CFDataCreate(0, 0, 0);
+ return emptyHistoryData;
+ }
+
+ // Ignores the date and item count limits; these are respected when loading instead of when saving, so
+ // that clients can learn of discarded items by listening to WebHistoryItemsDiscardedWhileLoadingNotification.
+ WebHistoryWriter writer(_entriesByDate);
+ writer.writePropertyList();
+ return [[(NSData *)writer.releaseData().get() retain] autorelease];
+}
+
+- (BOOL)saveToURL:(NSURL *)URL error:(NSError **)error
+{
+#if !LOG_DISABLED
+ double start = CFAbsoluteTimeGetCurrent();
+#endif
+
+ BOOL result = [[self data] writeToURL:URL options:0 error:error];
+
+#if !LOG_DISABLED
+ double duration = CFAbsoluteTimeGetCurrent() - start;
+ LOG(Timing, "saving history to %@ took %f seconds", URL, duration);
+#endif
+
+ return result;
+}
+
+- (void)addVisitedLinksToPageGroup:(PageGroup&)group
+{
+ NSEnumerator *enumerator = [_entriesByURL keyEnumerator];
+ while (NSString *url = [enumerator nextObject]) {
+ size_t length = [url length];
+ const UChar* characters = CFStringGetCharactersPtr(reinterpret_cast<CFStringRef>(url));
+ if (characters)
+ group.addVisitedLink(characters, length);
+ else {
+ Vector<UChar, 512> buffer(length);
+ [url getCharacters:buffer.data()];
+ group.addVisitedLink(buffer.data(), length);
+ }
+ }
+}
+
+@end
+
+@implementation WebHistory
+
++ (WebHistory *)optionalSharedHistory
+{
+ return _sharedHistory;
+}
+
++ (void)setOptionalSharedHistory:(WebHistory *)history
+{
+ if (_sharedHistory == history)
+ return;
+ // FIXME: Need to think about multiple instances of WebHistory per application
+ // and correct synchronization of history file between applications.
+ [_sharedHistory release];
+ _sharedHistory = [history retain];
+ PageGroup::setShouldTrackVisitedLinks(history);
+ PageGroup::removeAllVisitedLinks();
+}
+
+- (id)init
+{
+ self = [super init];
+ if (!self)
+ return nil;
+ _historyPrivate = [[WebHistoryPrivate alloc] init];
+ return self;
+}
+
+- (void)dealloc
+{
+ [_historyPrivate release];
+ [super dealloc];
+}
+
+// MARK: MODIFYING CONTENTS
+
+- (void)_sendNotification:(NSString *)name entries:(NSArray *)entries
+{
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:entries, WebHistoryItemsKey, nil];
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:name object:self userInfo:userInfo];
+}
+
+- (void)removeItems:(NSArray *)entries
+{
+ if ([_historyPrivate removeItems:entries]) {
+ [self _sendNotification:WebHistoryItemsRemovedNotification
+ entries:entries];
+ }
+}
+
+- (void)removeAllItems
+{
+ NSArray *entries = [_historyPrivate allItems];
+ if ([_historyPrivate removeAllItems])
+ [self _sendNotification:WebHistoryAllItemsRemovedNotification entries:entries];
+}
+
+- (void)addItems:(NSArray *)newEntries
+{
+ [_historyPrivate addItems:newEntries];
+ [self _sendNotification:WebHistoryItemsAddedNotification
+ entries:newEntries];
+}
+
+// MARK: DATE-BASED RETRIEVAL
+
+- (NSArray *)orderedLastVisitedDays
+{
+ return [_historyPrivate orderedLastVisitedDays];
+}
+
+- (NSArray *)orderedItemsLastVisitedOnDay:(NSCalendarDate *)date
+{
+ return [_historyPrivate orderedItemsLastVisitedOnDay:date];
+}
+
+// MARK: URL MATCHING
+
+- (BOOL)containsURL:(NSURL *)URL
+{
+ return [_historyPrivate containsURL:URL];
+}
+
+- (WebHistoryItem *)itemForURL:(NSURL *)URL
+{
+ return [_historyPrivate itemForURL:URL];
+}
+
+// MARK: SAVING TO DISK
+
+- (BOOL)loadFromURL:(NSURL *)URL error:(NSError **)error
+{
+ NSMutableArray *discardedItems = [[NSMutableArray alloc] init];
+ if (![_historyPrivate loadFromURL:URL collectDiscardedItemsInto:discardedItems error:error]) {
+ [discardedItems release];
+ return NO;
+ }
+
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:WebHistoryLoadedNotification
+ object:self];
+
+ if ([discardedItems count])
+ [self _sendNotification:WebHistoryItemsDiscardedWhileLoadingNotification entries:discardedItems];
+
+ [discardedItems release];
+ return YES;
+}
+
+- (BOOL)saveToURL:(NSURL *)URL error:(NSError **)error
+{
+ if (![_historyPrivate saveToURL:URL error:error])
+ return NO;
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:WebHistorySavedNotification
+ object:self];
+ return YES;
+}
+
+- (void)setHistoryItemLimit:(int)limit
+{
+ [_historyPrivate setHistoryItemLimit:limit];
+}
+
+- (int)historyItemLimit
+{
+ return [_historyPrivate historyItemLimit];
+}
+
+- (void)setHistoryAgeInDaysLimit:(int)limit
+{
+ [_historyPrivate setHistoryAgeInDaysLimit:limit];
+}
+
+- (int)historyAgeInDaysLimit
+{
+ return [_historyPrivate historyAgeInDaysLimit];
+}
+
+@end
+
+@implementation WebHistory (WebPrivate)
+
+- (WebHistoryItem *)_itemForURLString:(NSString *)URLString
+{
+ return [_historyPrivate itemForURLString:URLString];
+}
+
+- (NSArray *)allItems
+{
+ return [_historyPrivate allItems];
+}
+
+- (NSData *)_data
+{
+ return [_historyPrivate data];
+}
+
++ (void)_setVisitedLinkTrackingEnabled:(BOOL)visitedLinkTrackingEnabled
+{
+ PageGroup::setShouldTrackVisitedLinks(visitedLinkTrackingEnabled);
+}
+
++ (void)_removeAllVisitedLinks
+{
+ PageGroup::removeAllVisitedLinks();
+}
+
+@end
+
+@implementation WebHistory (WebInternal)
+
+- (void)_visitedURL:(NSURL *)url withTitle:(NSString *)title method:(NSString *)method wasFailure:(BOOL)wasFailure increaseVisitCount:(BOOL)increaseVisitCount
+{
+ WebHistoryItem *entry = [_historyPrivate visitedURL:url withTitle:title increaseVisitCount:increaseVisitCount];
+
+ HistoryItem* item = core(entry);
+ item->setLastVisitWasFailure(wasFailure);
+
+ if ([method length])
+ item->setLastVisitWasHTTPNonGet([method caseInsensitiveCompare:@"GET"] && (![[url scheme] caseInsensitiveCompare:@"http"] || ![[url scheme] caseInsensitiveCompare:@"https"]));
+
+ item->setRedirectURLs(0);
+
+ NSArray *entries = [[NSArray alloc] initWithObjects:entry, nil];
+ [self _sendNotification:WebHistoryItemsAddedNotification entries:entries];
+ [entries release];
+}
+
+- (void)_addVisitedLinksToPageGroup:(WebCore::PageGroup&)group
+{
+ [_historyPrivate addVisitedLinksToPageGroup:group];
+}
+
+@end
+
+WebHistoryWriter::WebHistoryWriter(DateToEntriesMap* entriesByDate)
+ : m_entriesByDate(entriesByDate)
+{
+ m_dateKeys.reserveCapacity(m_entriesByDate->size());
+ DateToEntriesMap::const_iterator end = m_entriesByDate->end();
+ for (DateToEntriesMap::const_iterator it = m_entriesByDate->begin(); it != end; ++it)
+ m_dateKeys.append(it->first);
+ sort(m_dateKeys.begin(), m_dateKeys.end());
+}
+
+void WebHistoryWriter::writeHistoryItems(BinaryPropertyListObjectStream& stream)
+{
+ for (int dateIndex = m_dateKeys.size() - 1; dateIndex >= 0; dateIndex--) {
+ NSArray *entries = m_entriesByDate->get(m_dateKeys[dateIndex]).get();
+ NSUInteger entryCount = [entries count];
+ for (NSUInteger entryIndex = 0; entryIndex < entryCount; ++entryIndex)
+ writeHistoryItem(stream, core([entries objectAtIndex:entryIndex]));
+ }
+}
diff --git a/Source/WebKit/mac/History/WebHistoryInternal.h b/Source/WebKit/mac/History/WebHistoryInternal.h
new file mode 100644
index 0000000..39d91d5
--- /dev/null
+++ b/Source/WebKit/mac/History/WebHistoryInternal.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "WebHistoryPrivate.h"
+
+namespace WebCore {
+ class PageGroup;
+}
+
+@interface WebHistory (WebInternal)
+- (void)_visitedURL:(NSURL *)URL withTitle:(NSString *)title method:(NSString *)method wasFailure:(BOOL)wasFailure increaseVisitCount:(BOOL)increaseVisitCount;
+- (void)_addVisitedLinksToPageGroup:(WebCore::PageGroup&)group;
+@end
diff --git a/Source/WebKit/mac/History/WebHistoryItem.h b/Source/WebKit/mac/History/WebHistoryItem.h
new file mode 100644
index 0000000..4de4344
--- /dev/null
+++ b/Source/WebKit/mac/History/WebHistoryItem.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@class WebHistoryItemPrivate;
+@class NSURL;
+
+/*
+ @discussion Notification sent when history item is modified.
+ @constant WebHistoryItemChanged Posted from whenever the value of
+ either the item's title, alternate title, url strings, or last visited interval
+ changes. The userInfo will be nil.
+*/
+extern NSString *WebHistoryItemChangedNotification;
+
+/*!
+ @class WebHistoryItem
+ @discussion WebHistoryItems are created by WebKit to represent pages visited.
+ The WebBackForwardList and WebHistory classes both use WebHistoryItems to represent
+ pages visited. With the exception of the displayTitle, the properties of
+ WebHistoryItems are set by WebKit. WebHistoryItems are normally never created directly.
+*/
+@interface WebHistoryItem : NSObject <NSCopying>
+{
+@private
+ WebHistoryItemPrivate *_private;
+}
+
+/*!
+ @method initWithURLString:title:lastVisitedTimeInterval:
+ @param URLString The URL string for the item.
+ @param title The title to use for the item. This is normally the <title> of a page.
+ @param time The time used to indicate when the item was used.
+ @abstract Initialize a new WebHistoryItem
+ @discussion WebHistoryItems are normally created for you by the WebKit.
+ You may use this method to prepopulate a WebBackForwardList, or create
+ 'artificial' items to add to a WebBackForwardList. When first initialized
+ the URLString and originalURLString will be the same.
+*/
+- (id)initWithURLString:(NSString *)URLString title:(NSString *)title lastVisitedTimeInterval:(NSTimeInterval)time;
+
+/*!
+ @method originalURLString
+ @abstract The string representation of the originial URL of this item.
+ This value is normally set by the WebKit.
+ @result The string corresponding to the initial URL of this item.
+*/
+- (NSString *)originalURLString;
+
+/*!
+ @method URLString
+ @abstract The string representation of the URL represented by this item.
+ @discussion The URLString may be different than the originalURLString if the page
+ redirected to a new location. This value is normally set by the WebKit.
+ @result The string corresponding to the final URL of this item.
+*/
+- (NSString *)URLString;
+
+
+/*!
+ @method title
+ @abstract The title of the page represented by this item.
+ @discussion This title cannot be changed by the client. This value
+ is normally set by the WebKit when a page title for the item is received.
+ @result The title of this item.
+*/
+- (NSString *)title;
+
+/*!
+ @method lastVisitedTimeInterval
+ @abstract The last time the page represented by this item was visited. The interval
+ is since the reference date as determined by NSDate. This value is normally set by
+ the WebKit.
+ @result The last time this item was visited.
+*/
+- (NSTimeInterval)lastVisitedTimeInterval;
+
+/*!
+ @method setAlternateTitle:
+ @param alternateTitle The new display title for this item.
+ @abstract A title that may be used by the client to display this item.
+*/
+- (void)setAlternateTitle:(NSString *)alternateTitle;
+
+/*
+ @method title
+ @abstract A title that may be used by the client to display this item.
+ @result The alternate title for this item.
+*/
+- (NSString *)alternateTitle;
+
+/*!
+ @method icon
+ @abstract The favorite icon of the page represented by this item.
+ @discussion This icon returned will be determined by the WebKit.
+ @result The icon associated with this item's URL.
+*/
+- (NSImage *)icon;
+
+@end
diff --git a/Source/WebKit/mac/History/WebHistoryItem.mm b/Source/WebKit/mac/History/WebHistoryItem.mm
new file mode 100644
index 0000000..530b18a
--- /dev/null
+++ b/Source/WebKit/mac/History/WebHistoryItem.mm
@@ -0,0 +1,659 @@
+/*
+ * Copyright (C) 2005, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "WebHistoryItemInternal.h"
+#import "WebHistoryItemPrivate.h"
+
+#import "WebFrameInternal.h"
+#import "WebFrameView.h"
+#import "WebHTMLViewInternal.h"
+#import "WebIconDatabase.h"
+#import "WebKitLogging.h"
+#import "WebKitNSStringExtras.h"
+#import "WebNSArrayExtras.h"
+#import "WebNSDictionaryExtras.h"
+#import "WebNSObjectExtras.h"
+#import "WebNSURLExtras.h"
+#import "WebNSURLRequestExtras.h"
+#import "WebNSViewExtras.h"
+#import "WebPluginController.h"
+#import "WebTypesInternal.h"
+#import <WebCore/HistoryItem.h>
+#import <WebCore/Image.h>
+#import <WebCore/KURL.h>
+#import <WebCore/PageCache.h>
+#import <WebCore/PlatformString.h>
+#import <WebCore/ThreadCheck.h>
+#import <WebCore/WebCoreObjCExtras.h>
+#import <runtime/InitializeThreading.h>
+#import <wtf/Assertions.h>
+#import <wtf/StdLibExtras.h>
+#import <wtf/Threading.h>
+
+// Private keys used in the WebHistoryItem's dictionary representation.
+// see 3245793 for explanation of "lastVisitedDate"
+static NSString *lastVisitedTimeIntervalKey = @"lastVisitedDate";
+static NSString *visitCountKey = @"visitCount";
+static NSString *titleKey = @"title";
+static NSString *childrenKey = @"children";
+static NSString *displayTitleKey = @"displayTitle";
+static NSString *lastVisitWasFailureKey = @"lastVisitWasFailure";
+static NSString *lastVisitWasHTTPNonGetKey = @"lastVisitWasHTTPNonGet";
+static NSString *redirectURLsKey = @"redirectURLs";
+static NSString *dailyVisitCountKey = @"D"; // short key to save space
+static NSString *weeklyVisitCountKey = @"W"; // short key to save space
+
+// Notification strings.
+NSString *WebHistoryItemChangedNotification = @"WebHistoryItemChangedNotification";
+
+using namespace WebCore;
+using namespace std;
+
+typedef HashMap<HistoryItem*, WebHistoryItem*> HistoryItemMap;
+
+static inline WebHistoryItemPrivate* kitPrivate(WebCoreHistoryItem* list) { return (WebHistoryItemPrivate*)list; }
+static inline WebCoreHistoryItem* core(WebHistoryItemPrivate* list) { return (WebCoreHistoryItem*)list; }
+
+static HistoryItemMap& historyItemWrappers()
+{
+ DEFINE_STATIC_LOCAL(HistoryItemMap, historyItemWrappers, ());
+ return historyItemWrappers;
+}
+
+void WKNotifyHistoryItemChanged(HistoryItem*)
+{
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:WebHistoryItemChangedNotification object:nil userInfo:nil];
+}
+
+@implementation WebHistoryItem
+
++ (void)initialize
+{
+ JSC::initializeThreading();
+ WTF::initializeMainThreadToProcessMainThread();
+#ifndef BUILDING_ON_TIGER
+ WebCoreObjCFinalizeOnMainThread(self);
+#endif
+}
+
+- (id)init
+{
+ return [self initWithWebCoreHistoryItem:HistoryItem::create()];
+}
+
+- (id)initWithURLString:(NSString *)URLString title:(NSString *)title lastVisitedTimeInterval:(NSTimeInterval)time
+{
+ WebCoreThreadViolationCheckRoundOne();
+ return [self initWithWebCoreHistoryItem:HistoryItem::create(URLString, title, time)];
+}
+
+- (void)dealloc
+{
+ if (WebCoreObjCScheduleDeallocateOnMainThread([WebHistoryItem class], self))
+ return;
+
+ if (_private) {
+ HistoryItem* coreItem = core(_private);
+ coreItem->deref();
+ historyItemWrappers().remove(coreItem);
+ }
+ [super dealloc];
+}
+
+- (void)finalize
+{
+ WebCoreThreadViolationCheckRoundOne();
+ // FIXME: ~HistoryItem is what releases the history item's icon from the icon database
+ // It's probably not good to release icons from the database only when the object is garbage-collected.
+ // Need to change design so this happens at a predictable time.
+ if (_private) {
+ HistoryItem* coreItem = core(_private);
+ coreItem->deref();
+ historyItemWrappers().remove(coreItem);
+ }
+ [super finalize];
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ WebCoreThreadViolationCheckRoundOne();
+ WebHistoryItem *copy = (WebHistoryItem *)NSCopyObject(self, 0, zone);
+ RefPtr<HistoryItem> item = core(_private)->copy();
+ copy->_private = kitPrivate(item.get());
+ historyItemWrappers().set(item.release().releaseRef(), copy);
+
+ return copy;
+}
+
+// FIXME: Need to decide if this class ever returns URLs and decide on the name of this method
+- (NSString *)URLString
+{
+ ASSERT_MAIN_THREAD();
+ return nsStringNilIfEmpty(core(_private)->urlString());
+}
+
+// The first URL we loaded to get to where this history item points. Includes both client
+// and server redirects.
+- (NSString *)originalURLString
+{
+ ASSERT_MAIN_THREAD();
+ return nsStringNilIfEmpty(core(_private)->originalURLString());
+}
+
+- (NSString *)title
+{
+ ASSERT_MAIN_THREAD();
+ return nsStringNilIfEmpty(core(_private)->title());
+}
+
+- (void)setAlternateTitle:(NSString *)alternateTitle
+{
+ core(_private)->setAlternateTitle(alternateTitle);
+}
+
+- (NSString *)alternateTitle
+{
+ return nsStringNilIfEmpty(core(_private)->alternateTitle());
+}
+
+- (NSImage *)icon
+{
+ return [[WebIconDatabase sharedIconDatabase] iconForURL:[self URLString] withSize:WebIconSmallSize];
+}
+
+- (NSTimeInterval)lastVisitedTimeInterval
+{
+ ASSERT_MAIN_THREAD();
+ return core(_private)->lastVisitedTime();
+}
+
+- (NSUInteger)hash
+{
+ return [(NSString*)core(_private)->urlString() hash];
+}
+
+- (BOOL)isEqual:(id)anObject
+{
+ ASSERT_MAIN_THREAD();
+ if (![anObject isMemberOfClass:[WebHistoryItem class]]) {
+ return NO;
+ }
+
+ return core(_private)->urlString() == core(((WebHistoryItem*)anObject)->_private)->urlString();
+}
+
+- (NSString *)description
+{
+ ASSERT_MAIN_THREAD();
+ HistoryItem* coreItem = core(_private);
+ NSMutableString *result = [NSMutableString stringWithFormat:@"%@ %@", [super description], (NSString*)coreItem->urlString()];
+ if (!coreItem->target().isEmpty()) {
+ NSString *target = coreItem->target();
+ [result appendFormat:@" in \"%@\"", target];
+ }
+ if (coreItem->isTargetItem()) {
+ [result appendString:@" *target*"];
+ }
+ if (coreItem->formData()) {
+ [result appendString:@" *POST*"];
+ }
+
+ if (coreItem->children().size()) {
+ const HistoryItemVector& children = coreItem->children();
+ int currPos = [result length];
+ unsigned size = children.size();
+ for (unsigned i = 0; i < size; ++i) {
+ WebHistoryItem *child = kit(children[i].get());
+ [result appendString:@"\n"];
+ [result appendString:[child description]];
+ }
+ // shift all the contents over. A bit slow, but hey, this is for debugging.
+ NSRange replRange = {currPos, [result length]-currPos};
+ [result replaceOccurrencesOfString:@"\n" withString:@"\n " options:0 range:replRange];
+ }
+
+ return result;
+}
+
+@end
+
+@interface WebWindowWatcher : NSObject
+@end
+
+
+@implementation WebHistoryItem (WebInternal)
+
+HistoryItem* core(WebHistoryItem *item)
+{
+ if (!item)
+ return 0;
+
+ ASSERT(historyItemWrappers().get(core(item->_private)) == item);
+
+ return core(item->_private);
+}
+
+WebHistoryItem *kit(HistoryItem* item)
+{
+ if (!item)
+ return nil;
+
+ WebHistoryItem *kitItem = historyItemWrappers().get(item);
+ if (kitItem)
+ return kitItem;
+
+ return [[[WebHistoryItem alloc] initWithWebCoreHistoryItem:item] autorelease];
+}
+
++ (WebHistoryItem *)entryWithURL:(NSURL *)URL
+{
+ return [[[self alloc] initWithURL:URL title:nil] autorelease];
+}
+
+static WebWindowWatcher *_windowWatcher = nil;
+
++ (void)initWindowWatcherIfNecessary
+{
+ if (_windowWatcher)
+ return;
+ _windowWatcher = [[WebWindowWatcher alloc] init];
+ [[NSNotificationCenter defaultCenter] addObserver:_windowWatcher selector:@selector(windowWillClose:)
+ name:NSWindowWillCloseNotification object:nil];
+}
+
+- (id)initWithURL:(NSURL *)URL target:(NSString *)target parent:(NSString *)parent title:(NSString *)title
+{
+ return [self initWithWebCoreHistoryItem:HistoryItem::create(URL, target, parent, title)];
+}
+
+- (id)initWithURLString:(NSString *)URLString title:(NSString *)title displayTitle:(NSString *)displayTitle lastVisitedTimeInterval:(NSTimeInterval)time
+{
+ return [self initWithWebCoreHistoryItem:HistoryItem::create(URLString, title, displayTitle, time)];
+}
+
+- (id)initWithWebCoreHistoryItem:(PassRefPtr<HistoryItem>)item
+{
+ WebCoreThreadViolationCheckRoundOne();
+ // Need to tell WebCore what function to call for the
+ // "History Item has Changed" notification - no harm in doing this
+ // everytime a WebHistoryItem is created
+ // Note: We also do this in [WebFrameView initWithFrame:] where we do
+ // other "init before WebKit is used" type things
+ WebCore::notifyHistoryItemChanged = WKNotifyHistoryItemChanged;
+
+ self = [super init];
+
+ _private = kitPrivate(item.releaseRef());
+ ASSERT(!historyItemWrappers().get(core(_private)));
+ historyItemWrappers().set(core(_private), self);
+ return self;
+}
+
+- (void)setTitle:(NSString *)title
+{
+ core(_private)->setTitle(title);
+}
+
+- (void)setVisitCount:(int)count
+{
+ core(_private)->setVisitCount(count);
+}
+
+- (void)setViewState:(id)statePList
+{
+ core(_private)->setViewState(statePList);
+}
+
+- (void)_mergeAutoCompleteHints:(WebHistoryItem *)otherItem
+{
+ ASSERT_ARG(otherItem, otherItem);
+ core(_private)->mergeAutoCompleteHints(core(otherItem->_private));
+}
+
+- (id)initFromDictionaryRepresentation:(NSDictionary *)dict
+{
+ ASSERT_MAIN_THREAD();
+ NSString *URLString = [dict _webkit_stringForKey:@""];
+ NSString *title = [dict _webkit_stringForKey:titleKey];
+
+ // Do an existence check to avoid calling doubleValue on a nil string. Leave
+ // time interval at 0 if there's no value in dict.
+ NSString *timeIntervalString = [dict _webkit_stringForKey:lastVisitedTimeIntervalKey];
+ NSTimeInterval lastVisited = timeIntervalString == nil ? 0 : [timeIntervalString doubleValue];
+
+ self = [self initWithURLString:URLString title:title displayTitle:[dict _webkit_stringForKey:displayTitleKey] lastVisitedTimeInterval:lastVisited];
+
+ // Check if we've read a broken URL from the file that has non-Latin1 chars. If so, try to convert
+ // as if it was from user typing.
+ if (![URLString canBeConvertedToEncoding:NSISOLatin1StringEncoding]) {
+ NSURL *tempURL = [NSURL _web_URLWithUserTypedString:URLString];
+ ASSERT(tempURL);
+ NSString *newURLString = [tempURL _web_originalDataAsString];
+ core(_private)->setURLString(newURLString);
+ core(_private)->setOriginalURLString(newURLString);
+ }
+
+ int visitCount = [dict _webkit_intForKey:visitCountKey];
+
+ // Can't trust data on disk, and we've had at least one report of this (<rdar://6572300>).
+ if (visitCount < 0) {
+ LOG_ERROR("visit count for history item \"%@\" is negative (%d), will be reset to 1", URLString, visitCount);
+ visitCount = 1;
+ }
+ core(_private)->setVisitCount(visitCount);
+
+ if ([dict _webkit_boolForKey:lastVisitWasFailureKey])
+ core(_private)->setLastVisitWasFailure(true);
+
+ BOOL lastVisitWasHTTPNonGet = [dict _webkit_boolForKey:lastVisitWasHTTPNonGetKey];
+ NSString *tempURLString = [URLString lowercaseString];
+ if (lastVisitWasHTTPNonGet && ([tempURLString hasPrefix:@"http:"] || [tempURLString hasPrefix:@"https:"]))
+ core(_private)->setLastVisitWasHTTPNonGet(lastVisitWasHTTPNonGet);
+
+ if (NSArray *redirectURLs = [dict _webkit_arrayForKey:redirectURLsKey]) {
+ NSUInteger size = [redirectURLs count];
+ OwnPtr<Vector<String> > redirectURLsVector(new Vector<String>(size));
+ for (NSUInteger i = 0; i < size; ++i)
+ (*redirectURLsVector)[i] = String([redirectURLs _webkit_stringAtIndex:i]);
+ core(_private)->setRedirectURLs(redirectURLsVector.release());
+ }
+
+ NSArray *dailyCounts = [dict _webkit_arrayForKey:dailyVisitCountKey];
+ NSArray *weeklyCounts = [dict _webkit_arrayForKey:weeklyVisitCountKey];
+ if (dailyCounts || weeklyCounts) {
+ Vector<int> coreDailyCounts([dailyCounts count]);
+ Vector<int> coreWeeklyCounts([weeklyCounts count]);
+
+ // Daily and weekly counts < 0 are errors in the data read from disk, so reset to 0.
+ for (size_t i = 0; i < coreDailyCounts.size(); ++i)
+ coreDailyCounts[i] = max([[dailyCounts _webkit_numberAtIndex:i] intValue], 0);
+ for (size_t i = 0; i < coreWeeklyCounts.size(); ++i)
+ coreWeeklyCounts[i] = max([[weeklyCounts _webkit_numberAtIndex:i] intValue], 0);
+
+ core(_private)->adoptVisitCounts(coreDailyCounts, coreWeeklyCounts);
+ }
+
+ NSArray *childDicts = [dict objectForKey:childrenKey];
+ if (childDicts) {
+ for (int i = [childDicts count] - 1; i >= 0; i--) {
+ WebHistoryItem *child = [[WebHistoryItem alloc] initFromDictionaryRepresentation:[childDicts objectAtIndex:i]];
+ core(_private)->addChildItem(core(child->_private));
+ [child release];
+ }
+ }
+
+ return self;
+}
+
+- (NSPoint)scrollPoint
+{
+ ASSERT_MAIN_THREAD();
+ return core(_private)->scrollPoint();
+}
+
+- (void)_visitedWithTitle:(NSString *)title increaseVisitCount:(BOOL)increaseVisitCount
+{
+ core(_private)->visited(title, [NSDate timeIntervalSinceReferenceDate], increaseVisitCount ? IncreaseVisitCount : DoNotIncreaseVisitCount);
+}
+
+- (void)_recordInitialVisit
+{
+ core(_private)->recordInitialVisit();
+}
+
+@end
+
+@implementation WebHistoryItem (WebPrivate)
+
+- (id)initWithURL:(NSURL *)URL title:(NSString *)title
+{
+ return [self initWithURLString:[URL _web_originalDataAsString] title:title lastVisitedTimeInterval:0];
+}
+
+- (NSDictionary *)dictionaryRepresentation
+{
+ ASSERT_MAIN_THREAD();
+ NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:8];
+
+ HistoryItem* coreItem = core(_private);
+
+ if (!coreItem->urlString().isEmpty())
+ [dict setObject:(NSString*)coreItem->urlString() forKey:@""];
+ if (!coreItem->title().isEmpty())
+ [dict setObject:(NSString*)coreItem->title() forKey:titleKey];
+ if (!coreItem->alternateTitle().isEmpty())
+ [dict setObject:(NSString*)coreItem->alternateTitle() forKey:displayTitleKey];
+ if (coreItem->lastVisitedTime() != 0.0) {
+ // Store as a string to maintain backward compatibility. (See 3245793)
+ [dict setObject:[NSString stringWithFormat:@"%.1lf", coreItem->lastVisitedTime()]
+ forKey:lastVisitedTimeIntervalKey];
+ }
+ if (coreItem->visitCount())
+ [dict setObject:[NSNumber numberWithInt:coreItem->visitCount()] forKey:visitCountKey];
+ if (coreItem->lastVisitWasFailure())
+ [dict setObject:[NSNumber numberWithBool:YES] forKey:lastVisitWasFailureKey];
+ if (coreItem->lastVisitWasHTTPNonGet()) {
+ ASSERT(coreItem->urlString().startsWith("http:", false) || coreItem->urlString().startsWith("https:", false));
+ [dict setObject:[NSNumber numberWithBool:YES] forKey:lastVisitWasHTTPNonGetKey];
+ }
+ if (Vector<String>* redirectURLs = coreItem->redirectURLs()) {
+ size_t size = redirectURLs->size();
+ ASSERT(size);
+ NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:size];
+ for (size_t i = 0; i < size; ++i)
+ [result addObject:(NSString*)redirectURLs->at(i)];
+ [dict setObject:result forKey:redirectURLsKey];
+ [result release];
+ }
+
+ const Vector<int>& dailyVisitCounts = coreItem->dailyVisitCounts();
+ if (dailyVisitCounts.size()) {
+ NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:13];
+ for (size_t i = 0; i < dailyVisitCounts.size(); ++i)
+ [array addObject:[NSNumber numberWithInt:dailyVisitCounts[i]]];
+ [dict setObject:array forKey:dailyVisitCountKey];
+ [array release];
+ }
+
+ const Vector<int>& weeklyVisitCounts = coreItem->weeklyVisitCounts();
+ if (weeklyVisitCounts.size()) {
+ NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:5];
+ for (size_t i = 0; i < weeklyVisitCounts.size(); ++i)
+ [array addObject:[NSNumber numberWithInt:weeklyVisitCounts[i]]];
+ [dict setObject:array forKey:weeklyVisitCountKey];
+ [array release];
+ }
+
+ if (coreItem->children().size()) {
+ const HistoryItemVector& children = coreItem->children();
+ NSMutableArray *childDicts = [NSMutableArray arrayWithCapacity:children.size()];
+
+ for (int i = children.size() - 1; i >= 0; i--)
+ [childDicts addObject:[kit(children[i].get()) dictionaryRepresentation]];
+ [dict setObject: childDicts forKey:childrenKey];
+ }
+
+ return dict;
+}
+
+- (NSString *)target
+{
+ ASSERT_MAIN_THREAD();
+ return nsStringNilIfEmpty(core(_private)->target());
+}
+
+- (BOOL)isTargetItem
+{
+ return core(_private)->isTargetItem();
+}
+
+- (int)visitCount
+{
+ ASSERT_MAIN_THREAD();
+ return core(_private)->visitCount();
+}
+
+- (NSString *)RSSFeedReferrer
+{
+ return nsStringNilIfEmpty(core(_private)->referrer());
+}
+
+- (void)setRSSFeedReferrer:(NSString *)referrer
+{
+ core(_private)->setReferrer(referrer);
+}
+
+- (NSArray *)children
+{
+ ASSERT_MAIN_THREAD();
+ const HistoryItemVector& children = core(_private)->children();
+ if (!children.size())
+ return nil;
+
+ unsigned size = children.size();
+ NSMutableArray *result = [[[NSMutableArray alloc] initWithCapacity:size] autorelease];
+
+ for (unsigned i = 0; i < size; ++i)
+ [result addObject:kit(children[i].get())];
+
+ return result;
+}
+
+- (void)setAlwaysAttemptToUsePageCache:(BOOL)flag
+{
+ // Safari 2.0 uses this for SnapBack, so we stub it out to avoid a crash.
+}
+
+- (NSURL *)URL
+{
+ ASSERT_MAIN_THREAD();
+ const KURL& url = core(_private)->url();
+ if (url.isEmpty())
+ return nil;
+ return url;
+}
+
+// This should not be called directly for WebHistoryItems that are already included
+// in WebHistory. Use -[WebHistory setLastVisitedTimeInterval:forItem:] instead.
+- (void)_setLastVisitedTimeInterval:(NSTimeInterval)time
+{
+ core(_private)->setLastVisitedTime(time);
+}
+
+// FIXME: <rdar://problem/4880065> - Push Global History into WebCore
+// Once that task is complete, this accessor can go away
+- (NSCalendarDate *)_lastVisitedDate
+{
+ ASSERT_MAIN_THREAD();
+ return [[[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:core(_private)->lastVisitedTime()] autorelease];
+}
+
+- (WebHistoryItem *)targetItem
+{
+ ASSERT_MAIN_THREAD();
+ return kit(core(_private)->targetItem());
+}
+
++ (void)_releaseAllPendingPageCaches
+{
+ pageCache()->releaseAutoreleasedPagesNow();
+}
+
+- (id)_transientPropertyForKey:(NSString *)key
+{
+ return core(_private)->getTransientProperty(key);
+}
+
+- (void)_setTransientProperty:(id)property forKey:(NSString *)key
+{
+ core(_private)->setTransientProperty(key, property);
+}
+
+- (BOOL)lastVisitWasFailure
+{
+ return core(_private)->lastVisitWasFailure();
+}
+
+- (void)_setLastVisitWasFailure:(BOOL)failure
+{
+ core(_private)->setLastVisitWasFailure(failure);
+}
+
+- (BOOL)_lastVisitWasHTTPNonGet
+{
+ return core(_private)->lastVisitWasHTTPNonGet();
+}
+
+- (NSArray *)_redirectURLs
+{
+ Vector<String>* redirectURLs = core(_private)->redirectURLs();
+ if (!redirectURLs)
+ return nil;
+
+ size_t size = redirectURLs->size();
+ ASSERT(size);
+ NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:size];
+ for (size_t i = 0; i < size; ++i)
+ [result addObject:(NSString*)redirectURLs->at(i)];
+ return [result autorelease];
+}
+
+- (size_t)_getDailyVisitCounts:(const int**)counts
+{
+ HistoryItem* coreItem = core(_private);
+ *counts = coreItem->dailyVisitCounts().data();
+ return coreItem->dailyVisitCounts().size();
+}
+
+- (size_t)_getWeeklyVisitCounts:(const int**)counts
+{
+ HistoryItem* coreItem = core(_private);
+ *counts = coreItem->weeklyVisitCounts().data();
+ return coreItem->weeklyVisitCounts().size();
+}
+
+@end
+
+
+// FIXME: <rdar://problem/4886761>.
+// This is a bizarre policy. We flush the page caches ANY time ANY window is closed?
+
+@implementation WebWindowWatcher
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+ if (!pthread_main_np()) {
+ [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
+ return;
+ }
+
+ pageCache()->releaseAutoreleasedPagesNow();
+}
+
+@end
diff --git a/Source/WebKit/mac/History/WebHistoryItemInternal.h b/Source/WebKit/mac/History/WebHistoryItemInternal.h
new file mode 100644
index 0000000..d324682
--- /dev/null
+++ b/Source/WebKit/mac/History/WebHistoryItemInternal.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "WebBackForwardList.h"
+#import "WebHistoryItemPrivate.h"
+#import <wtf/Forward.h>
+
+namespace WebCore {
+ class HistoryItem;
+}
+
+WebCore::HistoryItem* core(WebHistoryItem *item);
+WebHistoryItem *kit(WebCore::HistoryItem* item);
+
+extern void WKNotifyHistoryItemChanged(WebCore::HistoryItem*);
+
+@interface WebHistoryItem (WebInternal)
+
++ (WebHistoryItem *)entryWithURL:(NSURL *)URL;
++ (void)initWindowWatcherIfNecessary;
+
+- (id)initWithURL:(NSURL *)URL target:(NSString *)target parent:(NSString *)parent title:(NSString *)title;
+- (id)initWithURLString:(NSString *)URLString title:(NSString *)title displayTitle:(NSString *)displayTitle lastVisitedTimeInterval:(NSTimeInterval)time;
+- (id)initFromDictionaryRepresentation:(NSDictionary *)dict;
+- (id)initWithWebCoreHistoryItem:(PassRefPtr<WebCore::HistoryItem>)item;
+
+- (void)_mergeAutoCompleteHints:(WebHistoryItem *)otherItem;
+- (void)setTitle:(NSString *)title;
+- (void)_visitedWithTitle:(NSString *)title increaseVisitCount:(BOOL)increaseVisitCount;
+- (void)_recordInitialVisit;
+
+@end
+
+@interface WebBackForwardList (WebInternal)
+- (void)_close;
+@end
diff --git a/Source/WebKit/mac/History/WebHistoryItemPrivate.h b/Source/WebKit/mac/History/WebHistoryItemPrivate.h
new file mode 100644
index 0000000..0c079a7
--- /dev/null
+++ b/Source/WebKit/mac/History/WebHistoryItemPrivate.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <WebKit/WebHistoryItem.h>
+
+@interface WebHistoryItem (WebPrivate)
+
++ (void)_releaseAllPendingPageCaches;
+
+- (id)initWithURL:(NSURL *)URL title:(NSString *)title;
+
+- (NSURL *)URL;
+- (int)visitCount;
+- (BOOL)lastVisitWasFailure;
+- (void)_setLastVisitWasFailure:(BOOL)failure;
+
+- (BOOL)_lastVisitWasHTTPNonGet;
+
+- (NSString *)RSSFeedReferrer;
+- (void)setRSSFeedReferrer:(NSString *)referrer;
+- (NSCalendarDate *)_lastVisitedDate;
+
+- (NSArray *)_redirectURLs;
+
+- (WebHistoryItem *)targetItem;
+- (NSString *)target;
+- (BOOL)isTargetItem;
+- (NSArray *)children;
+- (NSDictionary *)dictionaryRepresentation;
+
+// This should not be called directly for WebHistoryItems that are already included
+// in WebHistory. Use -[WebHistory setLastVisitedTimeInterval:forItem:] instead.
+- (void)_setLastVisitedTimeInterval:(NSTimeInterval)time;
+// Transient properties may be of any ObjC type. They are intended to be used to store state per back/forward list entry.
+// The properties will not be persisted; when the history item is removed, the properties will be lost.
+- (id)_transientPropertyForKey:(NSString *)key;
+- (void)_setTransientProperty:(id)property forKey:(NSString *)key;
+
+- (size_t)_getDailyVisitCounts:(const int**)counts;
+- (size_t)_getWeeklyVisitCounts:(const int**)counts;
+
+@end
diff --git a/Source/WebKit/mac/History/WebHistoryPrivate.h b/Source/WebKit/mac/History/WebHistoryPrivate.h
new file mode 100644
index 0000000..b93472f
--- /dev/null
+++ b/Source/WebKit/mac/History/WebHistoryPrivate.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2005, 2008, 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <WebKit/WebHistory.h>
+
+/*
+ @constant WebHistoryItemsDiscardedWhileLoadingNotification Posted from loadFromURL:error:.
+ This notification comes with a userInfo dictionary that contains the array of
+ items discarded due to the date limit or item limit. The key for the array is WebHistoryItemsKey.
+*/
+// FIXME: This notification should become public API.
+extern NSString *WebHistoryItemsDiscardedWhileLoadingNotification;
+
+@interface WebHistory (WebPrivate)
+
+// FIXME: The following SPI is used by Safari. Should it be made into public API?
+- (WebHistoryItem *)_itemForURLString:(NSString *)URLString;
+
+/*!
+ @method allItems
+ @result Returns an array of all WebHistoryItems in WebHistory, in an undefined order.
+*/
+- (NSArray *)allItems;
+
+/*!
+ @method _data
+ @result A data object with the entire history in the same format used by the saveToURL:error: method.
+*/
+- (NSData *)_data;
+
++ (void)_setVisitedLinkTrackingEnabled:(BOOL)visitedLinkTrackingEnabled;
++ (void)_removeAllVisitedLinks;
+@end
diff --git a/Source/WebKit/mac/History/WebURLsWithTitles.h b/Source/WebKit/mac/History/WebURLsWithTitles.h
new file mode 100644
index 0000000..905588e
--- /dev/null
+++ b/Source/WebKit/mac/History/WebURLsWithTitles.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#define WebURLsWithTitlesPboardType @"WebURLsWithTitlesPboardType"
+
+// Convenience class for getting URLs and associated titles on and off an NSPasteboard
+
+@interface WebURLsWithTitles : NSObject
+
+// Writes parallel arrays of URLs and titles to the pasteboard. These items can be retrieved by
+// calling URLsFromPasteboard and titlesFromPasteboard. URLs must consist of NSURL objects.
+// titles must consist of NSStrings, or be nil. If titles is nil, or if titles is a different
+// length than URLs, empty strings will be used for all titles. If URLs is nil, this method
+// returns without doing anything. You must declare an WebURLsWithTitlesPboardType data type
+// for pasteboard before invoking this method, or it will return without doing anything.
++ (void)writeURLs:(NSArray *)URLs andTitles:(NSArray *)titles toPasteboard:(NSPasteboard *)pasteboard;
+
+// Reads an array of NSURLs off the pasteboard. Returns nil if pasteboard does not contain
+// data of type WebURLsWithTitlesPboardType. This array consists of the URLs that correspond to
+// the titles returned from titlesFromPasteboard.
++ (NSArray *)URLsFromPasteboard:(NSPasteboard *)pasteboard;
+
+// Reads an array of NSStrings off the pasteboard. Returns nil if pasteboard does not contain
+// data of type WebURLsWithTitlesPboardType. This array consists of the titles that correspond to
+// the URLs returned from URLsFromPasteboard.
++ (NSArray *)titlesFromPasteboard:(NSPasteboard *)pasteboard;
+
+@end
diff --git a/Source/WebKit/mac/History/WebURLsWithTitles.m b/Source/WebKit/mac/History/WebURLsWithTitles.m
new file mode 100644
index 0000000..df9ef75
--- /dev/null
+++ b/Source/WebKit/mac/History/WebURLsWithTitles.m
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "WebURLsWithTitles.h"
+
+#import <WebKit/WebNSURLExtras.h>
+#import <WebKit/WebKitNSStringExtras.h>
+
+@implementation WebURLsWithTitles
+
++ (NSArray *)arrayWithIFURLsWithTitlesPboardType
+{
+ // Make a canned array so we don't construct it on the fly over and over.
+ static NSArray *cannedArray = nil;
+
+ if (cannedArray == nil) {
+ cannedArray = [[NSArray arrayWithObject:WebURLsWithTitlesPboardType] retain];
+ }
+
+ return cannedArray;
+}
+
++(void)writeURLs:(NSArray *)URLs andTitles:(NSArray *)titles toPasteboard:(NSPasteboard *)pasteboard
+{
+ NSMutableArray *URLStrings;
+ NSMutableArray *titlesOrEmptyStrings;
+ unsigned index, count;
+
+ count = [URLs count];
+ if (count == 0) {
+ return;
+ }
+
+ if ([pasteboard availableTypeFromArray:[self arrayWithIFURLsWithTitlesPboardType]] == nil) {
+ return;
+ }
+
+ if (count != [titles count]) {
+ titles = nil;
+ }
+
+ URLStrings = [NSMutableArray arrayWithCapacity:count];
+ titlesOrEmptyStrings = [NSMutableArray arrayWithCapacity:count];
+ for (index = 0; index < count; ++index) {
+ [URLStrings addObject:[[URLs objectAtIndex:index] _web_originalDataAsString]];
+ [titlesOrEmptyStrings addObject:(titles == nil) ? @"" : [[titles objectAtIndex:index] _webkit_stringByTrimmingWhitespace]];
+ }
+
+ [pasteboard setPropertyList:[NSArray arrayWithObjects:URLStrings, titlesOrEmptyStrings, nil]
+ forType:WebURLsWithTitlesPboardType];
+}
+
++(NSArray *)titlesFromPasteboard:(NSPasteboard *)pasteboard
+{
+ if ([pasteboard availableTypeFromArray:[self arrayWithIFURLsWithTitlesPboardType]] == nil) {
+ return nil;
+ }
+
+ return [[pasteboard propertyListForType:WebURLsWithTitlesPboardType] objectAtIndex:1];
+}
+
++(NSArray *)URLsFromPasteboard:(NSPasteboard *)pasteboard
+{
+ NSArray *URLStrings;
+ NSMutableArray *URLs;
+ unsigned index, count;
+
+ if ([pasteboard availableTypeFromArray:[self arrayWithIFURLsWithTitlesPboardType]] == nil) {
+ return nil;
+ }
+
+ URLStrings = [[pasteboard propertyListForType:WebURLsWithTitlesPboardType] objectAtIndex:0];
+ count = [URLStrings count];
+ URLs = [NSMutableArray arrayWithCapacity:count];
+ for (index = 0; index < count; ++index) {
+ [URLs addObject:[NSURL _web_URLWithDataAsString:[URLStrings objectAtIndex:index]]];
+ }
+
+ return URLs;
+}
+
+@end