2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "WebKitDLL.h"
28 #include "WebHistoryItem.h"
30 #include "COMEnumVariant.h"
31 #include "MarshallingHelpers.h"
33 #include <WebCore/BString.h>
34 #include <WebCore/COMPtr.h>
35 #include <WebCore/HistoryItem.h>
36 #include <WebCore/KURL.h>
37 #include <wtf/PassOwnPtr.h>
38 #include <wtf/RetainPtr.h>
39 #include <wtf/text/CString.h>
41 using namespace WebCore;
43 // WebHistoryItem ----------------------------------------------------------------
45 static HashMap<HistoryItem*, WebHistoryItem*>& historyItemWrappers()
47 static HashMap<HistoryItem*, WebHistoryItem*> staticHistoryItemWrappers;
48 return staticHistoryItemWrappers;
51 WebHistoryItem::WebHistoryItem(PassRefPtr<HistoryItem> historyItem)
53 , m_historyItem(historyItem)
55 ASSERT(!historyItemWrappers().contains(m_historyItem.get()));
56 historyItemWrappers().set(m_historyItem.get(), this);
59 gClassNameCount.add("WebHistoryItem");
62 WebHistoryItem::~WebHistoryItem()
64 ASSERT(historyItemWrappers().contains(m_historyItem.get()));
65 historyItemWrappers().remove(m_historyItem.get());
68 gClassNameCount.remove("WebHistoryItem");
71 WebHistoryItem* WebHistoryItem::createInstance()
73 WebHistoryItem* instance = new WebHistoryItem(HistoryItem::create());
78 WebHistoryItem* WebHistoryItem::createInstance(PassRefPtr<HistoryItem> historyItem)
80 WebHistoryItem* instance;
82 instance = historyItemWrappers().get(historyItem.get());
85 instance = new WebHistoryItem(historyItem);
91 // IWebHistoryItemPrivate -----------------------------------------------------
93 static CFStringRef urlKey = CFSTR("");
94 static CFStringRef lastVisitedDateKey = CFSTR("lastVisitedDate");
95 static CFStringRef titleKey = CFSTR("title");
96 static CFStringRef visitCountKey = CFSTR("visitCount");
97 static CFStringRef lastVisitWasFailureKey = CFSTR("lastVisitWasFailure");
98 static CFStringRef lastVisitWasHTTPNonGetKey = CFSTR("lastVisitWasHTTPNonGet");
99 static CFStringRef redirectURLsKey = CFSTR("redirectURLs");
100 static CFStringRef dailyVisitCountKey = CFSTR("D"); // short key to save space
101 static CFStringRef weeklyVisitCountKey = CFSTR("W"); // short key to save space
103 HRESULT STDMETHODCALLTYPE WebHistoryItem::initFromDictionaryRepresentation(void* dictionary)
105 CFDictionaryRef dictionaryRef = (CFDictionaryRef) dictionary;
107 CFStringRef urlStringRef = (CFStringRef) CFDictionaryGetValue(dictionaryRef, urlKey);
108 if (urlStringRef && CFGetTypeID(urlStringRef) != CFStringGetTypeID())
111 CFStringRef lastVisitedRef = (CFStringRef) CFDictionaryGetValue(dictionaryRef, lastVisitedDateKey);
112 if (!lastVisitedRef || CFGetTypeID(lastVisitedRef) != CFStringGetTypeID())
114 CFAbsoluteTime lastVisitedTime = CFStringGetDoubleValue(lastVisitedRef);
116 CFStringRef titleRef = (CFStringRef) CFDictionaryGetValue(dictionaryRef, titleKey);
117 if (titleRef && CFGetTypeID(titleRef) != CFStringGetTypeID())
120 CFNumberRef visitCountRef = (CFNumberRef) CFDictionaryGetValue(dictionaryRef, visitCountKey);
121 if (!visitCountRef || CFGetTypeID(visitCountRef) != CFNumberGetTypeID())
123 int visitedCount = 0;
124 if (!CFNumberGetValue(visitCountRef, kCFNumberIntType, &visitedCount))
127 // Can't trust data on disk, and we've had at least one report of this (<rdar://6572300>).
128 if (visitedCount < 0) {
129 LOG_ERROR("visit count for history item \"%s\" is negative (%d), will be reset to 1", String(urlStringRef).utf8().data(), visitedCount);
133 CFBooleanRef lastVisitWasFailureRef = static_cast<CFBooleanRef>(CFDictionaryGetValue(dictionaryRef, lastVisitWasFailureKey));
134 if (lastVisitWasFailureRef && CFGetTypeID(lastVisitWasFailureRef) != CFBooleanGetTypeID())
136 bool lastVisitWasFailure = lastVisitWasFailureRef && CFBooleanGetValue(lastVisitWasFailureRef);
138 CFBooleanRef lastVisitWasHTTPNonGetRef = static_cast<CFBooleanRef>(CFDictionaryGetValue(dictionaryRef, lastVisitWasHTTPNonGetKey));
139 if (lastVisitWasHTTPNonGetRef && CFGetTypeID(lastVisitWasHTTPNonGetRef) != CFBooleanGetTypeID())
141 bool lastVisitWasHTTPNonGet = lastVisitWasHTTPNonGetRef && CFBooleanGetValue(lastVisitWasHTTPNonGetRef);
143 OwnPtr<Vector<String> > redirectURLsVector;
144 if (CFArrayRef redirectURLsRef = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionaryRef, redirectURLsKey))) {
145 CFIndex size = CFArrayGetCount(redirectURLsRef);
146 redirectURLsVector = adoptPtr(new Vector<String>(size));
147 for (CFIndex i = 0; i < size; ++i)
148 (*redirectURLsVector)[i] = String(static_cast<CFStringRef>(CFArrayGetValueAtIndex(redirectURLsRef, i)));
151 CFArrayRef dailyCounts = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionaryRef, dailyVisitCountKey));
152 if (dailyCounts && CFGetTypeID(dailyCounts) != CFArrayGetTypeID())
154 CFArrayRef weeklyCounts = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionaryRef, weeklyVisitCountKey));
155 if (weeklyCounts && CFGetTypeID(weeklyCounts) != CFArrayGetTypeID())
158 std::auto_ptr<Vector<int> > dailyVector, weeklyVector;
159 if (dailyCounts || weeklyCounts) {
160 CFIndex dailySize = dailyCounts ? CFArrayGetCount(dailyCounts) : 0;
161 CFIndex weeklySize = weeklyCounts ? CFArrayGetCount(weeklyCounts) : 0;
162 dailyVector.reset(new Vector<int>(dailySize));
163 weeklyVector.reset(new Vector<int>(weeklySize));
165 // Daily and weekly counts < 0 are errors in the data read from disk, so reset to 0.
166 for (CFIndex i = 0; i < dailySize; ++i) {
167 CFNumberRef dailyCount = static_cast<CFNumberRef>(CFArrayGetValueAtIndex(dailyCounts, i));
168 if (CFGetTypeID(dailyCount) == CFNumberGetTypeID())
169 CFNumberGetValue(dailyCount, kCFNumberIntType, &(*dailyVector)[i]);
170 if ((*dailyVector)[i] < 0)
171 (*dailyVector)[i] = 0;
173 for (CFIndex i = 0; i < weeklySize; ++i) {
174 CFNumberRef weeklyCount = static_cast<CFNumberRef>(CFArrayGetValueAtIndex(weeklyCounts, i));
175 if (CFGetTypeID(weeklyCount) == CFNumberGetTypeID())
176 CFNumberGetValue(weeklyCount, kCFNumberIntType, &(*weeklyVector)[i]);
177 if ((*weeklyVector)[i] < 0)
178 (*weeklyVector)[i] = 0;
182 historyItemWrappers().remove(m_historyItem.get());
183 m_historyItem = HistoryItem::create(urlStringRef, titleRef, lastVisitedTime);
184 historyItemWrappers().set(m_historyItem.get(), this);
186 m_historyItem->setVisitCount(visitedCount);
187 if (lastVisitWasFailure)
188 m_historyItem->setLastVisitWasFailure(true);
190 if (lastVisitWasHTTPNonGet && (protocolIs(m_historyItem->urlString(), "http") || protocolIs(m_historyItem->urlString(), "https")))
191 m_historyItem->setLastVisitWasHTTPNonGet(true);
193 if (redirectURLsVector)
194 m_historyItem->setRedirectURLs(redirectURLsVector.release());
196 if (dailyVector.get())
197 m_historyItem->adoptVisitCounts(*dailyVector, *weeklyVector);
202 HRESULT STDMETHODCALLTYPE WebHistoryItem::dictionaryRepresentation(void** dictionary)
204 CFDictionaryRef* dictionaryRef = (CFDictionaryRef*) dictionary;
205 static CFStringRef lastVisitedFormat = CFSTR("%.1lf");
206 CFStringRef lastVisitedStringRef =
207 CFStringCreateWithFormat(0, 0, lastVisitedFormat, m_historyItem->lastVisitedTime());
208 if (!lastVisitedStringRef)
215 if (!m_historyItem->urlString().isEmpty()) {
216 keys[keyCount] = urlKey;
217 values[keyCount++] = m_historyItem->urlString().createCFString();
220 keys[keyCount] = lastVisitedDateKey;
221 values[keyCount++] = lastVisitedStringRef;
223 if (!m_historyItem->title().isEmpty()) {
224 keys[keyCount] = titleKey;
225 values[keyCount++] = m_historyItem->title().createCFString();
228 keys[keyCount] = visitCountKey;
229 int visitCount = m_historyItem->visitCount();
230 values[keyCount++] = CFNumberCreate(0, kCFNumberIntType, &visitCount);
232 if (m_historyItem->lastVisitWasFailure()) {
233 keys[keyCount] = lastVisitWasFailureKey;
234 values[keyCount++] = CFRetain(kCFBooleanTrue);
237 if (m_historyItem->lastVisitWasHTTPNonGet()) {
238 ASSERT(m_historyItem->urlString().startsWith("http:", false) || m_historyItem->urlString().startsWith("https:", false));
239 keys[keyCount] = lastVisitWasHTTPNonGetKey;
240 values[keyCount++] = CFRetain(kCFBooleanTrue);
243 if (Vector<String>* redirectURLs = m_historyItem->redirectURLs()) {
244 size_t size = redirectURLs->size();
246 CFStringRef* items = new CFStringRef[size];
247 for (size_t i = 0; i < size; ++i)
248 items[i] = redirectURLs->at(i).createCFString();
249 CFArrayRef result = CFArrayCreate(0, (const void**)items, size, &kCFTypeArrayCallBacks);
250 for (size_t i = 0; i < size; ++i)
254 keys[keyCount] = redirectURLsKey;
255 values[keyCount++] = result;
258 const Vector<int>& dailyVisitCount(m_historyItem->dailyVisitCounts());
259 if (size_t size = dailyVisitCount.size()) {
260 Vector<CFNumberRef, 13> numbers(size);
261 for (size_t i = 0; i < size; ++i)
262 numbers[i] = CFNumberCreate(0, kCFNumberIntType, &dailyVisitCount[i]);
264 CFArrayRef result = CFArrayCreate(0, (const void**)numbers.data(), size, &kCFTypeArrayCallBacks);
266 for (size_t i = 0; i < size; ++i)
267 CFRelease(numbers[i]);
269 keys[keyCount] = dailyVisitCountKey;
270 values[keyCount++] = result;
273 const Vector<int>& weeklyVisitCount(m_historyItem->weeklyVisitCounts());
274 if (size_t size = weeklyVisitCount.size()) {
275 Vector<CFNumberRef, 5> numbers(size);
276 for (size_t i = 0; i < size; ++i)
277 numbers[i] = CFNumberCreate(0, kCFNumberIntType, &weeklyVisitCount[i]);
279 CFArrayRef result = CFArrayCreate(0, (const void**)numbers.data(), size, &kCFTypeArrayCallBacks);
281 for (size_t i = 0; i < size; ++i)
282 CFRelease(numbers[i]);
284 keys[keyCount] = weeklyVisitCountKey;
285 values[keyCount++] = result;
288 *dictionaryRef = CFDictionaryCreate(0, keys, values, keyCount, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
290 for (int i = 0; i < keyCount; ++i)
291 CFRelease(values[i]);
296 HRESULT STDMETHODCALLTYPE WebHistoryItem::hasURLString(BOOL *hasURL)
298 *hasURL = m_historyItem->urlString().isEmpty() ? FALSE : TRUE;
302 HRESULT STDMETHODCALLTYPE WebHistoryItem::visitCount(int *count)
304 *count = m_historyItem->visitCount();
308 HRESULT STDMETHODCALLTYPE WebHistoryItem::setVisitCount(int count)
310 m_historyItem->setVisitCount(count);
314 HRESULT STDMETHODCALLTYPE WebHistoryItem::mergeAutoCompleteHints(IWebHistoryItem* otherItem)
319 COMPtr<WebHistoryItem> otherWebHistoryItem(Query, otherItem);
320 if (!otherWebHistoryItem)
323 m_historyItem->mergeAutoCompleteHints(otherWebHistoryItem->historyItem());
328 HRESULT STDMETHODCALLTYPE WebHistoryItem::setLastVisitedTimeInterval(DATE time)
330 m_historyItem->setLastVisitedTime(MarshallingHelpers::DATEToCFAbsoluteTime(time));
334 HRESULT STDMETHODCALLTYPE WebHistoryItem::setTitle(BSTR title)
336 m_historyItem->setTitle(String(title, SysStringLen(title)));
341 HRESULT STDMETHODCALLTYPE WebHistoryItem::RSSFeedReferrer(BSTR* url)
343 BString str(m_historyItem->referrer());
344 *url = str.release();
349 HRESULT STDMETHODCALLTYPE WebHistoryItem::setRSSFeedReferrer(BSTR url)
351 m_historyItem->setReferrer(String(url, SysStringLen(url)));
356 HRESULT STDMETHODCALLTYPE WebHistoryItem::hasPageCache(BOOL* /*hasCache*/)
359 ASSERT_NOT_REACHED();
363 HRESULT STDMETHODCALLTYPE WebHistoryItem::setHasPageCache(BOOL /*hasCache*/)
369 HRESULT STDMETHODCALLTYPE WebHistoryItem::target(BSTR* target)
372 ASSERT_NOT_REACHED();
376 *target = BString(m_historyItem->target()).release();
380 HRESULT STDMETHODCALLTYPE WebHistoryItem::isTargetItem(BOOL* result)
383 ASSERT_NOT_REACHED();
387 *result = m_historyItem->isTargetItem() ? TRUE : FALSE;
391 HRESULT STDMETHODCALLTYPE WebHistoryItem::children(unsigned* outChildCount, SAFEARRAY** outChildren)
393 if (!outChildCount || !outChildren) {
394 ASSERT_NOT_REACHED();
401 const HistoryItemVector& coreChildren = m_historyItem->children();
402 if (coreChildren.isEmpty())
404 size_t childCount = coreChildren.size();
406 SAFEARRAY* children = SafeArrayCreateVector(VT_UNKNOWN, 0, static_cast<ULONG>(childCount));
408 return E_OUTOFMEMORY;
410 for (unsigned i = 0; i < childCount; ++i) {
411 COMPtr<WebHistoryItem> item(AdoptCOM, WebHistoryItem::createInstance(coreChildren[i]));
413 SafeArrayDestroy(children);
414 return E_OUTOFMEMORY;
418 HRESULT hr = SafeArrayPutElement(children, &longI, item.get());
420 SafeArrayDestroy(children);
425 *outChildCount = static_cast<unsigned>(childCount);
426 *outChildren = children;
431 HRESULT STDMETHODCALLTYPE WebHistoryItem::lastVisitWasFailure(BOOL* wasFailure)
434 ASSERT_NOT_REACHED();
438 *wasFailure = m_historyItem->lastVisitWasFailure();
442 HRESULT STDMETHODCALLTYPE WebHistoryItem::setLastVisitWasFailure(BOOL wasFailure)
444 m_historyItem->setLastVisitWasFailure(wasFailure);
448 HRESULT STDMETHODCALLTYPE WebHistoryItem::lastVisitWasHTTPNonGet(BOOL* HTTPNonGet)
451 ASSERT_NOT_REACHED();
455 *HTTPNonGet = m_historyItem->lastVisitWasHTTPNonGet();
460 HRESULT STDMETHODCALLTYPE WebHistoryItem::setLastVisitWasHTTPNonGet(BOOL HTTPNonGet)
462 m_historyItem->setLastVisitWasHTTPNonGet(HTTPNonGet);
466 HRESULT STDMETHODCALLTYPE WebHistoryItem::redirectURLs(IEnumVARIANT** urls)
469 ASSERT_NOT_REACHED();
473 Vector<String>* urlVector = m_historyItem->redirectURLs();
479 COMPtr<COMEnumVariant<Vector<String> > > enumVariant(AdoptCOM, COMEnumVariant<Vector<String> >::createInstance(*urlVector));
480 *urls = enumVariant.leakRef();
485 HRESULT STDMETHODCALLTYPE WebHistoryItem::visitedWithTitle(BSTR title, BOOL increaseVisitCount)
487 m_historyItem->visited(title, CFAbsoluteTimeGetCurrent(), increaseVisitCount ? IncreaseVisitCount : DoNotIncreaseVisitCount);
491 HRESULT STDMETHODCALLTYPE WebHistoryItem::getDailyVisitCounts(int* number, int** counts)
493 if (!number || !counts) {
494 ASSERT_NOT_REACHED();
498 *counts = const_cast<int*>(m_historyItem->dailyVisitCounts().data());
499 *number = m_historyItem->dailyVisitCounts().size();
503 HRESULT STDMETHODCALLTYPE WebHistoryItem::getWeeklyVisitCounts(int* number, int** counts)
505 if (!number || !counts) {
506 ASSERT_NOT_REACHED();
510 *counts = const_cast<int*>(m_historyItem->weeklyVisitCounts().data());
511 *number = m_historyItem->weeklyVisitCounts().size();
515 HRESULT STDMETHODCALLTYPE WebHistoryItem::recordInitialVisit()
517 m_historyItem->recordInitialVisit();
521 // IUnknown -------------------------------------------------------------------
523 HRESULT STDMETHODCALLTYPE WebHistoryItem::QueryInterface(REFIID riid, void** ppvObject)
526 if (IsEqualGUID(riid, __uuidof(WebHistoryItem)))
528 else if (IsEqualGUID(riid, IID_IUnknown))
529 *ppvObject = static_cast<IWebHistoryItem*>(this);
530 else if (IsEqualGUID(riid, IID_IWebHistoryItem))
531 *ppvObject = static_cast<IWebHistoryItem*>(this);
532 else if (IsEqualGUID(riid, IID_IWebHistoryItemPrivate))
533 *ppvObject = static_cast<IWebHistoryItemPrivate*>(this);
535 return E_NOINTERFACE;
541 ULONG STDMETHODCALLTYPE WebHistoryItem::AddRef(void)
546 ULONG STDMETHODCALLTYPE WebHistoryItem::Release(void)
548 ULONG newRef = --m_refCount;
555 // IWebHistoryItem -------------------------------------------------------------
557 HRESULT STDMETHODCALLTYPE WebHistoryItem::initWithURLString(
558 /* [in] */ BSTR urlString,
559 /* [in] */ BSTR title,
560 /* [in] */ DATE lastVisited)
562 historyItemWrappers().remove(m_historyItem.get());
563 m_historyItem = HistoryItem::create(String(urlString, SysStringLen(urlString)), String(title, SysStringLen(title)), MarshallingHelpers::DATEToCFAbsoluteTime(lastVisited));
564 historyItemWrappers().set(m_historyItem.get(), this);
569 HRESULT STDMETHODCALLTYPE WebHistoryItem::originalURLString(
570 /* [retval][out] */ BSTR* url)
575 BString str = m_historyItem->originalURLString();
576 *url = str.release();
580 HRESULT STDMETHODCALLTYPE WebHistoryItem::URLString(
581 /* [retval][out] */ BSTR* url)
586 BString str = m_historyItem->urlString();
587 *url = str.release();
591 HRESULT STDMETHODCALLTYPE WebHistoryItem::title(
592 /* [retval][out] */ BSTR* pageTitle)
597 BString str(m_historyItem->title());
598 *pageTitle = str.release();
602 HRESULT STDMETHODCALLTYPE WebHistoryItem::lastVisitedTimeInterval(
603 /* [retval][out] */ DATE* lastVisited)
608 *lastVisited = MarshallingHelpers::CFAbsoluteTimeToDATE(m_historyItem->lastVisitedTime());
612 HRESULT STDMETHODCALLTYPE WebHistoryItem::setAlternateTitle(
613 /* [in] */ BSTR title)
615 m_alternateTitle = String(title, SysStringLen(title));
619 HRESULT STDMETHODCALLTYPE WebHistoryItem::alternateTitle(
620 /* [retval][out] */ BSTR* title)
623 ASSERT_NOT_REACHED();
627 *title = BString(m_alternateTitle).release();
631 HRESULT STDMETHODCALLTYPE WebHistoryItem::icon(
632 /* [out, retval] */ OLE_HANDLE* /*hBitmap*/)
634 ASSERT_NOT_REACHED();
638 // WebHistoryItem -------------------------------------------------------------
640 HistoryItem* WebHistoryItem::historyItem() const
642 return m_historyItem.get();