summaryrefslogtreecommitdiffstats
path: root/WebKit/win/WebHistory.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebKit/win/WebHistory.cpp')
-rw-r--r--WebKit/win/WebHistory.cpp314
1 files changed, 187 insertions, 127 deletions
diff --git a/WebKit/win/WebHistory.cpp b/WebKit/win/WebHistory.cpp
index e647e01..095a74b 100644
--- a/WebKit/win/WebHistory.cpp
+++ b/WebKit/win/WebHistory.cpp
@@ -28,6 +28,7 @@
#include "WebHistory.h"
#include "CFDictionaryPropertyBag.h"
+#include "MemoryStream.h"
#include "WebKit.h"
#include "MarshallingHelpers.h"
#include "WebHistoryItem.h"
@@ -36,20 +37,59 @@
#include "WebPreferences.h"
#include <CoreFoundation/CoreFoundation.h>
#pragma warning( push, 0 )
-#include <wtf/Vector.h>
+#include <WebCore/HistoryItem.h>
+#include <WebCore/HistoryPropertyList.h>
#include <WebCore/KURL.h>
#include <WebCore/PageGroup.h>
-#include <WebCore/HistoryItem.h>
+#include <WebCore/SharedBuffer.h>
#pragma warning( pop )
+#include <functional>
#include <wtf/StdLibExtras.h>
+#include <wtf/Vector.h>
using namespace WebCore;
+using namespace std;
CFStringRef DatesArrayKey = CFSTR("WebHistoryDates");
CFStringRef FileVersionKey = CFSTR("WebHistoryFileVersion");
#define currentFileVersion 1
+class WebHistoryWriter : public HistoryPropertyListWriter {
+public:
+ WebHistoryWriter(const WebHistory::DateToEntriesMap&);
+
+private:
+ virtual void writeHistoryItems(BinaryPropertyListObjectStream&);
+
+ const WebHistory::DateToEntriesMap& m_entriesByDate;
+ Vector<WebHistory::DateKey> m_dateKeys;
+};
+
+WebHistoryWriter::WebHistoryWriter(const WebHistory::DateToEntriesMap& entriesByDate)
+ : m_entriesByDate(entriesByDate)
+{
+ copyKeysToVector(m_entriesByDate, m_dateKeys);
+ sort(m_dateKeys.begin(), m_dateKeys.end());
+}
+
+void WebHistoryWriter::writeHistoryItems(BinaryPropertyListObjectStream& stream)
+{
+ for (int dateIndex = m_dateKeys.size() - 1; dateIndex >= 0; --dateIndex) {
+ // get the entries for that date
+ CFArrayRef entries = m_entriesByDate.get(m_dateKeys[dateIndex]).get();
+ CFIndex entriesCount = CFArrayGetCount(entries);
+ for (CFIndex j = entriesCount - 1; j >= 0; --j) {
+ IWebHistoryItem* item = (IWebHistoryItem*) CFArrayGetValueAtIndex(entries, j);
+ COMPtr<WebHistoryItem> webItem(Query, item);
+ if (!webItem)
+ continue;
+
+ writeHistoryItem(stream, webItem->historyItem());
+ }
+ }
+}
+
static bool areEqualOrClose(double d1, double d2)
{
double diff = d1-d2;
@@ -96,8 +136,6 @@ WebHistory::WebHistory()
gClassNameCount.add("WebHistory");
m_entriesByURL.adoptCF(CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &MarshallingHelpers::kIUnknownDictionaryValueCallBacks));
- m_datesWithEntries.adoptCF(CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks));
- m_entriesByDate.adoptCF(CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks));
m_preferences = WebPreferences::sharedStandardPreferences();
}
@@ -348,77 +386,34 @@ HRESULT WebHistory::saveHistoryGuts(CFURLRef url, IWebError** error)
{
HRESULT hr = S_OK;
- // FIXME: Correctly report error when new API is ready.
+ // FIXME: Correctly report error when new API is ready.
if (error)
*error = 0;
+ RetainPtr<CFDataRef> data = this->data();
+
RetainPtr<CFWriteStreamRef> stream(AdoptCF, CFWriteStreamCreateWithFile(kCFAllocatorDefault, url));
if (!stream)
return E_FAIL;
- CFMutableArrayRef rawEntries;
- hr = datesArray(&rawEntries);
- if (FAILED(hr))
- return hr;
- RetainPtr<CFMutableArrayRef> entries(AdoptCF, rawEntries);
-
- // create the outer dictionary
- CFTypeRef keys[2];
- CFTypeRef values[2];
- keys[0] = DatesArrayKey;
- values[0] = entries.get();
- keys[1] = FileVersionKey;
-
- int version = currentFileVersion;
- RetainPtr<CFNumberRef> versionCF(AdoptCF, CFNumberCreate(0, kCFNumberIntType, &version));
- values[1] = versionCF.get();
-
- RetainPtr<CFDictionaryRef> dictionary(AdoptCF,
- CFDictionaryCreate(0, keys, values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
-
if (!CFWriteStreamOpen(stream.get()))
return E_FAIL;
- if (!CFPropertyListWriteToStream(dictionary.get(), stream.get(), kCFPropertyListXMLFormat_v1_0, 0))
- hr = E_FAIL;
-
- CFWriteStreamClose(stream.get());
+ const UInt8* dataPtr = CFDataGetBytePtr(data.get());
+ CFIndex length = CFDataGetLength(data.get());
- return hr;
-}
-
-HRESULT WebHistory::datesArray(CFMutableArrayRef* datesArray)
-{
- HRESULT hr = S_OK;
-
- RetainPtr<CFMutableArrayRef> result(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks));
-
- // for each date with entries
- int dateCount = CFArrayGetCount(m_entriesByDate.get());
- for (int i = 0; i < dateCount; ++i) {
- // get the entries for that date
- CFArrayRef entries = (CFArrayRef)CFArrayGetValueAtIndex(m_entriesByDate.get(), i);
- int entriesCount = CFArrayGetCount(entries);
- for (int j = entriesCount - 1; j >= 0; --j) {
- IWebHistoryItem* item = (IWebHistoryItem*) CFArrayGetValueAtIndex(entries, j);
- IWebHistoryItemPrivate* webHistoryItem;
- hr = item->QueryInterface(IID_IWebHistoryItemPrivate, (void**)&webHistoryItem);
- if (FAILED(hr))
- return E_FAIL;
-
- CFDictionaryRef itemDict;
- hr = webHistoryItem->dictionaryRepresentation((void**)&itemDict);
- webHistoryItem->Release();
- if (FAILED(hr))
- return E_FAIL;
-
- CFArrayAppendValue(result.get(), itemDict);
- CFRelease(itemDict);
+ while (length) {
+ CFIndex bytesWritten = CFWriteStreamWrite(stream.get(), dataPtr, length);
+ if (bytesWritten <= 0) {
+ hr = E_FAIL;
+ break;
}
+ dataPtr += bytesWritten;
+ length -= bytesWritten;
}
- if (SUCCEEDED(hr))
- *datesArray = result.releaseRef();
+ CFWriteStreamClose(stream.get());
+
return hr;
}
@@ -458,8 +453,8 @@ HRESULT STDMETHODCALLTYPE WebHistory::removeItems(
HRESULT STDMETHODCALLTYPE WebHistory::removeAllItems( void)
{
- CFArrayRemoveAllValues(m_entriesByDate.get());
- CFArrayRemoveAllValues(m_datesWithEntries.get());
+ m_entriesByDate.clear();
+ m_orderedLastVisitedDays.clear();
CFIndex itemCount = CFDictionaryGetCount(m_entriesByURL.get());
Vector<IWebHistoryItem*> itemsVector(itemCount);
@@ -478,7 +473,7 @@ HRESULT STDMETHODCALLTYPE WebHistory::orderedLastVisitedDays(
/* [out][in] */ int* count,
/* [in] */ DATE* calendarDates)
{
- int dateCount = CFArrayGetCount(m_datesWithEntries.get());
+ int dateCount = m_entriesByDate.size();
if (!calendarDates) {
*count = dateCount;
return S_OK;
@@ -490,14 +485,17 @@ HRESULT STDMETHODCALLTYPE WebHistory::orderedLastVisitedDays(
}
*count = dateCount;
- for (int i = 0; i < dateCount; i++) {
- CFNumberRef absoluteTimeNumberRef = (CFNumberRef)CFArrayGetValueAtIndex(m_datesWithEntries.get(), i);
- CFAbsoluteTime absoluteTime;
- if (!CFNumberGetValue(absoluteTimeNumberRef, kCFNumberDoubleType, &absoluteTime))
- return E_FAIL;
- calendarDates[i] = MarshallingHelpers::CFAbsoluteTimeToDATE(absoluteTime);
+ if (!m_orderedLastVisitedDays) {
+ m_orderedLastVisitedDays.set(new DATE[dateCount]);
+ DateToEntriesMap::const_iterator::Keys end = m_entriesByDate.end().keys();
+ int i = 0;
+ for (DateToEntriesMap::const_iterator::Keys it = m_entriesByDate.begin().keys(); it != end; ++it, ++i)
+ m_orderedLastVisitedDays[i] = MarshallingHelpers::CFAbsoluteTimeToDATE(*it);
+ // Use std::greater to sort the days in descending order (i.e., most-recent first).
+ sort(m_orderedLastVisitedDays.get(), m_orderedLastVisitedDays.get() + dateCount, greater<DATE>());
}
+ memcpy(calendarDates, m_orderedLastVisitedDays.get(), dateCount * sizeof(DATE));
return S_OK;
}
@@ -506,13 +504,13 @@ HRESULT STDMETHODCALLTYPE WebHistory::orderedItemsLastVisitedOnDay(
/* [in] */ IWebHistoryItem** items,
/* [in] */ DATE calendarDate)
{
- int index;
- if (!findIndex(&index, MarshallingHelpers::DATEToCFAbsoluteTime(calendarDate))) {
+ DateKey dateKey;
+ if (!findKey(&dateKey, MarshallingHelpers::DATEToCFAbsoluteTime(calendarDate))) {
*count = 0;
return 0;
}
- CFArrayRef entries = (CFArrayRef)CFArrayGetValueAtIndex(m_entriesByDate.get(), index);
+ CFArrayRef entries = m_entriesByDate.get(dateKey).get();
if (!entries) {
*count = 0;
return 0;
@@ -534,7 +532,7 @@ HRESULT STDMETHODCALLTYPE WebHistory::orderedItemsLastVisitedOnDay(
for (int i = 0; i < newCount; i++) {
IWebHistoryItem* item = (IWebHistoryItem*)CFArrayGetValueAtIndex(entries, i);
item->AddRef();
- items[newCount - i - 1] = item; // reverse when inserting to get the list sorted oldest to newest
+ items[i] = item;
}
return S_OK;
@@ -564,6 +562,21 @@ HRESULT STDMETHODCALLTYPE WebHistory::allItems(
return S_OK;
}
+HRESULT WebHistory::data(IStream** stream)
+{
+ if (!stream)
+ return E_POINTER;
+
+ *stream = 0;
+
+ RetainPtr<CFDataRef> historyData = data();
+ if (!historyData)
+ return S_OK;
+
+ COMPtr<MemoryStream> result = MemoryStream::createInstance(SharedBuffer::wrapCFData(historyData.get()));
+ return result.copyRefTo(stream);
+}
+
HRESULT STDMETHODCALLTYPE WebHistory::setHistoryItemLimit(
/* [in] */ int limit)
{
@@ -681,7 +694,7 @@ HRESULT WebHistory::addItem(IWebHistoryItem* entry, bool discardDuplicate, bool*
return hr;
}
-void WebHistory::visitedURL(const KURL& url, const String& title, const String& httpMethod, bool wasFailure)
+void WebHistory::visitedURL(const KURL& url, const String& title, const String& httpMethod, bool wasFailure, bool increaseVisitCount)
{
RetainPtr<CFStringRef> urlString(AdoptCF, url.string().createCFString());
@@ -694,7 +707,7 @@ void WebHistory::visitedURL(const KURL& url, const String& title, const String&
// Remove the item from date caches before changing its last visited date. Otherwise we might get duplicate entries
// as seen in <rdar://problem/6570573>.
removeItemFromDateCaches(entry);
- entryPrivate->visitedWithTitle(BString(title));
+ entryPrivate->visitedWithTitle(BString(title), increaseVisitCount);
} else {
COMPtr<WebHistoryItem> item(AdoptCOM, WebHistoryItem::createInstance());
if (!item)
@@ -724,10 +737,10 @@ void WebHistory::visitedURL(const KURL& url, const String& title, const String&
entryPrivate->setLastVisitWasFailure(wasFailure);
if (!httpMethod.isEmpty())
- entryPrivate->setLastVisitWasHTTPNonGet(!equalIgnoringCase(httpMethod, "GET") && (url.protocolIs("http") || url.protocolIs("https")));
+ entryPrivate->setLastVisitWasHTTPNonGet(!equalIgnoringCase(httpMethod, "GET") && url.protocolInHTTPFamily());
COMPtr<WebHistoryItem> item(Query, entry);
- item->historyItem()->setRedirectURLs(std::auto_ptr<Vector<String> >());
+ item->historyItem()->setRedirectURLs(0);
CFDictionaryPropertyBag* userInfo = createUserInfoFromHistoryItem(
getNotificationString(kWebHistoryItemsAddedNotification), entry);
@@ -792,20 +805,20 @@ HRESULT WebHistory::addItemToDateCaches(IWebHistoryItem* entry)
DATE lastVisitedCOMTime;
entry->lastVisitedTimeInterval(&lastVisitedCOMTime);
- CFAbsoluteTime lastVisitedDate = timeToDate(MarshallingHelpers::DATEToCFAbsoluteTime(lastVisitedCOMTime));
- int dateIndex;
- if (findIndex(&dateIndex, lastVisitedDate)) {
+ DateKey dateKey;
+ if (findKey(&dateKey, MarshallingHelpers::DATEToCFAbsoluteTime(lastVisitedCOMTime))) {
// other entries already exist for this date
- hr = insertItem(entry, dateIndex);
+ hr = insertItem(entry, dateKey);
} else {
+ ASSERT(!m_entriesByDate.contains(dateKey));
// no other entries exist for this date
- RetainPtr<CFNumberRef> lastVisitedDateRef(AdoptCF, CFNumberCreate(0, kCFNumberDoubleType, &lastVisitedDate));
- CFArrayInsertValueAtIndex(m_datesWithEntries.get(), dateIndex, lastVisitedDateRef.get());
RetainPtr<CFMutableArrayRef> entryArray(AdoptCF,
CFArrayCreateMutable(0, 0, &MarshallingHelpers::kIUnknownArrayCallBacks));
CFArrayAppendValue(entryArray.get(), entry);
- CFArrayInsertValueAtIndex(m_entriesByDate.get(), dateIndex, entryArray.get());
+ m_entriesByDate.set(dateKey, entryArray);
+ // Clear m_orderedLastVisitedDays so it will be regenerated when next requested.
+ m_orderedLastVisitedDays.clear();
}
return hr;
@@ -817,13 +830,15 @@ HRESULT WebHistory::removeItemFromDateCaches(IWebHistoryItem* entry)
DATE lastVisitedCOMTime;
entry->lastVisitedTimeInterval(&lastVisitedCOMTime);
- CFAbsoluteTime lastVisitedDate = timeToDate(MarshallingHelpers::DATEToCFAbsoluteTime(lastVisitedCOMTime));
- int dateIndex;
- if (!findIndex(&dateIndex, lastVisitedDate))
+ DateKey dateKey;
+ if (!findKey(&dateKey, MarshallingHelpers::DATEToCFAbsoluteTime(lastVisitedCOMTime)))
return E_FAIL;
- CFMutableArrayRef entriesForDate = (CFMutableArrayRef) CFArrayGetValueAtIndex(m_entriesByDate.get(), dateIndex);
+ DateToEntriesMap::iterator found = m_entriesByDate.find(dateKey);
+ ASSERT(found != m_entriesByDate.end());
+ CFMutableArrayRef entriesForDate = found->second.get();
+
CFIndex count = CFArrayGetCount(entriesForDate);
for (int i = count - 1; i >= 0; --i) {
if ((IWebHistoryItem*)CFArrayGetValueAtIndex(entriesForDate, i) == entry)
@@ -832,60 +847,95 @@ HRESULT WebHistory::removeItemFromDateCaches(IWebHistoryItem* entry)
// remove this date entirely if there are no other entries on it
if (CFArrayGetCount(entriesForDate) == 0) {
- CFArrayRemoveValueAtIndex(m_entriesByDate.get(), dateIndex);
- CFArrayRemoveValueAtIndex(m_datesWithEntries.get(), dateIndex);
+ m_entriesByDate.remove(found);
+ // Clear m_orderedLastVisitedDays so it will be regenerated when next requested.
+ m_orderedLastVisitedDays.clear();
}
return hr;
}
+WebHistory::DateKey timeIntervalForBeginningOfDay(CFAbsoluteTime day)
+{
+ RetainPtr<CFTimeZoneRef> timeZone(AdoptCF, CFTimeZoneCopyDefault());
+ CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(day, timeZone.get());
+ date.hour = 0;
+ date.minute = 0;
+ date.second = 0;
+ CFAbsoluteTime result = CFGregorianDateGetAbsoluteTime(date, timeZone.get());
+
+ // Converting from double to int64_t is safe here as NSDate's useful range
+ // is -2**48 .. 2**47 which will safely fit in an int64_t.
+ return static_cast<WebHistory::DateKey>(result);
+}
+
// Returns whether the day is already in the list of days,
-// and fills in *index with the found or proposed index.
-bool WebHistory::findIndex(int* index, CFAbsoluteTime forDay)
-{
- CFAbsoluteTime forDayInDays = timeToDate(forDay);
-
- //FIXME: just does linear search through days; inefficient if many days
- int count = CFArrayGetCount(m_datesWithEntries.get());
- for (*index = 0; *index < count; ++*index) {
- CFNumberRef entryTimeNumberRef = (CFNumberRef) CFArrayGetValueAtIndex(m_datesWithEntries.get(), *index);
- CFAbsoluteTime entryTime;
- CFNumberGetValue(entryTimeNumberRef, kCFNumberDoubleType, &entryTime);
- CFAbsoluteTime entryInDays = timeToDate(entryTime);
- if (areEqualOrClose(forDayInDays, entryInDays))
- return true;
- else if (forDayInDays > entryInDays)
- return false;
- }
- return false;
+// and fills in *key with the found or proposed key.
+bool WebHistory::findKey(DateKey* key, CFAbsoluteTime forDay)
+{
+ ASSERT_ARG(key, key);
+
+ *key = timeIntervalForBeginningOfDay(forDay);
+ return m_entriesByDate.contains(*key);
}
-HRESULT WebHistory::insertItem(IWebHistoryItem* entry, int dateIndex)
+HRESULT WebHistory::insertItem(IWebHistoryItem* entry, DateKey dateKey)
{
+ ASSERT_ARG(entry, entry);
+ ASSERT_ARG(dateKey, m_entriesByDate.contains(dateKey));
+
HRESULT hr = S_OK;
if (!entry)
return E_FAIL;
- if (dateIndex < 0 || dateIndex >= CFArrayGetCount(m_entriesByDate.get()))
- return E_FAIL;
- //FIXME: just does linear search through entries; inefficient if many entries for this date
DATE entryTime;
entry->lastVisitedTimeInterval(&entryTime);
- CFMutableArrayRef entriesForDate = (CFMutableArrayRef) CFArrayGetValueAtIndex(m_entriesByDate.get(), dateIndex);
- int count = CFArrayGetCount(entriesForDate);
- // optimized for inserting oldest to youngest
- int index;
- for (index = 0; index < count; ++index) {
- IWebHistoryItem* indEntry = (IWebHistoryItem*) CFArrayGetValueAtIndex(entriesForDate, index);
- DATE indTime;
- hr = indEntry->lastVisitedTimeInterval(&indTime);
- if (FAILED(hr))
- return hr;
- if (entryTime < indTime)
- break;
- }
- CFArrayInsertValueAtIndex(entriesForDate, index, entry);
+ CFMutableArrayRef entriesForDate = m_entriesByDate.get(dateKey).get();
+ unsigned count = CFArrayGetCount(entriesForDate);
+
+ // 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
+ bool isNewerThanAllEntries = false;
+ if (count) {
+ IWebHistoryItem* item = const_cast<IWebHistoryItem*>(static_cast<const IWebHistoryItem*>(CFArrayGetValueAtIndex(entriesForDate, 0)));
+ DATE itemTime;
+ isNewerThanAllEntries = SUCCEEDED(item->lastVisitedTimeInterval(&itemTime)) && itemTime < entryTime;
+ }
+ if (!count || isNewerThanAllEntries) {
+ CFArrayInsertValueAtIndex(entriesForDate, 0, entry);
+ return S_OK;
+ }
+
+ // .. or older than all existing entries
+ bool isOlderThanAllEntries = false;
+ if (count > 0) {
+ IWebHistoryItem* item = const_cast<IWebHistoryItem*>(static_cast<const IWebHistoryItem*>(CFArrayGetValueAtIndex(entriesForDate, count - 1)));
+ DATE itemTime;
+ isOlderThanAllEntries = SUCCEEDED(item->lastVisitedTimeInterval(&itemTime)) && itemTime >= entryTime;
+ }
+ if (isOlderThanAllEntries) {
+ CFArrayInsertValueAtIndex(entriesForDate, count, entry);
+ return S_OK;
+ }
+
+ unsigned low = 0;
+ unsigned high = count;
+ while (low < high) {
+ unsigned mid = low + (high - low) / 2;
+ IWebHistoryItem* item = const_cast<IWebHistoryItem*>(static_cast<const IWebHistoryItem*>(CFArrayGetValueAtIndex(entriesForDate, mid)));
+ DATE itemTime;
+ if (FAILED(item->lastVisitedTimeInterval(&itemTime)))
+ return E_FAIL;
+
+ if (itemTime >= entryTime)
+ low = mid + 1;
+ else
+ high = mid;
+ }
+
+ // low is now the index of the first entry that is older than entryDate
+ CFArrayInsertValueAtIndex(entriesForDate, low, entry);
return S_OK;
}
@@ -940,3 +990,13 @@ void WebHistory::addVisitedLinksToPageGroup(PageGroup& group)
{
CFDictionaryApplyFunction(m_entriesByURL.get(), addVisitedLinkToPageGroup, &group);
}
+
+RetainPtr<CFDataRef> WebHistory::data() const
+{
+ if (m_entriesByDate.isEmpty())
+ return 0;
+
+ WebHistoryWriter writer(m_entriesByDate);
+ writer.writePropertyList();
+ return writer.releaseData();
+}