diff options
Diffstat (limited to 'WebKit/win/WebHistory.cpp')
-rw-r--r-- | WebKit/win/WebHistory.cpp | 314 |
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(); +} |