1/*
2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
12 *
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.
24 */
25
26#include "config.h"
27#include "WebKitDLL.h"
28#include "WebHistoryItem.h"
29
30#include "COMEnumVariant.h"
31#include "MarshallingHelpers.h"
32#include "WebKit.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>
40
41using namespace WebCore;
42
43// WebHistoryItem ----------------------------------------------------------------
44
45static HashMap<HistoryItem*, WebHistoryItem*>& historyItemWrappers()
46{
47    static HashMap<HistoryItem*, WebHistoryItem*> staticHistoryItemWrappers;
48    return staticHistoryItemWrappers;
49}
50
51WebHistoryItem::WebHistoryItem(PassRefPtr<HistoryItem> historyItem)
52: m_refCount(0)
53, m_historyItem(historyItem)
54{
55    ASSERT(!historyItemWrappers().contains(m_historyItem.get()));
56    historyItemWrappers().set(m_historyItem.get(), this);
57
58    gClassCount++;
59    gClassNameCount.add("WebHistoryItem");
60}
61
62WebHistoryItem::~WebHistoryItem()
63{
64    ASSERT(historyItemWrappers().contains(m_historyItem.get()));
65    historyItemWrappers().remove(m_historyItem.get());
66
67    gClassCount--;
68    gClassNameCount.remove("WebHistoryItem");
69}
70
71WebHistoryItem* WebHistoryItem::createInstance()
72{
73    WebHistoryItem* instance = new WebHistoryItem(HistoryItem::create());
74    instance->AddRef();
75    return instance;
76}
77
78WebHistoryItem* WebHistoryItem::createInstance(PassRefPtr<HistoryItem> historyItem)
79{
80    WebHistoryItem* instance;
81
82    instance = historyItemWrappers().get(historyItem.get());
83
84    if (!instance)
85        instance = new WebHistoryItem(historyItem);
86
87    instance->AddRef();
88    return instance;
89}
90
91// IWebHistoryItemPrivate -----------------------------------------------------
92
93static CFStringRef urlKey = CFSTR("");
94static CFStringRef lastVisitedDateKey = CFSTR("lastVisitedDate");
95static CFStringRef titleKey = CFSTR("title");
96static CFStringRef visitCountKey = CFSTR("visitCount");
97static CFStringRef lastVisitWasFailureKey = CFSTR("lastVisitWasFailure");
98static CFStringRef lastVisitWasHTTPNonGetKey = CFSTR("lastVisitWasHTTPNonGet");
99static CFStringRef redirectURLsKey = CFSTR("redirectURLs");
100static CFStringRef dailyVisitCountKey = CFSTR("D"); // short key to save space
101static CFStringRef weeklyVisitCountKey = CFSTR("W"); // short key to save space
102
103HRESULT STDMETHODCALLTYPE WebHistoryItem::initFromDictionaryRepresentation(void* dictionary)
104{
105    CFDictionaryRef dictionaryRef = (CFDictionaryRef) dictionary;
106
107    CFStringRef urlStringRef = (CFStringRef) CFDictionaryGetValue(dictionaryRef, urlKey);
108    if (urlStringRef && CFGetTypeID(urlStringRef) != CFStringGetTypeID())
109        return E_FAIL;
110
111    CFStringRef lastVisitedRef = (CFStringRef) CFDictionaryGetValue(dictionaryRef, lastVisitedDateKey);
112    if (!lastVisitedRef || CFGetTypeID(lastVisitedRef) != CFStringGetTypeID())
113        return E_FAIL;
114    CFAbsoluteTime lastVisitedTime = CFStringGetDoubleValue(lastVisitedRef);
115
116    CFStringRef titleRef = (CFStringRef) CFDictionaryGetValue(dictionaryRef, titleKey);
117    if (titleRef && CFGetTypeID(titleRef) != CFStringGetTypeID())
118        return E_FAIL;
119
120    CFNumberRef visitCountRef = (CFNumberRef) CFDictionaryGetValue(dictionaryRef, visitCountKey);
121    if (!visitCountRef || CFGetTypeID(visitCountRef) != CFNumberGetTypeID())
122        return E_FAIL;
123    int visitedCount = 0;
124    if (!CFNumberGetValue(visitCountRef, kCFNumberIntType, &visitedCount))
125        return E_FAIL;
126
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);
130        visitedCount = 1;
131    }
132
133    CFBooleanRef lastVisitWasFailureRef = static_cast<CFBooleanRef>(CFDictionaryGetValue(dictionaryRef, lastVisitWasFailureKey));
134    if (lastVisitWasFailureRef && CFGetTypeID(lastVisitWasFailureRef) != CFBooleanGetTypeID())
135        return E_FAIL;
136    bool lastVisitWasFailure = lastVisitWasFailureRef && CFBooleanGetValue(lastVisitWasFailureRef);
137
138    CFBooleanRef lastVisitWasHTTPNonGetRef = static_cast<CFBooleanRef>(CFDictionaryGetValue(dictionaryRef, lastVisitWasHTTPNonGetKey));
139    if (lastVisitWasHTTPNonGetRef && CFGetTypeID(lastVisitWasHTTPNonGetRef) != CFBooleanGetTypeID())
140        return E_FAIL;
141    bool lastVisitWasHTTPNonGet = lastVisitWasHTTPNonGetRef && CFBooleanGetValue(lastVisitWasHTTPNonGetRef);
142
143    OwnPtr<Vector<String> > redirectURLsVector;
144    if (CFArrayRef redirectURLsRef = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionaryRef, redirectURLsKey))) {
145        CFIndex size = CFArrayGetCount(redirectURLsRef);
146        redirectURLsVector = PassOwnPtr<Vector<String> >(new Vector<String>(size));
147        for (CFIndex i = 0; i < size; ++i)
148            (*redirectURLsVector)[i] = String(static_cast<CFStringRef>(CFArrayGetValueAtIndex(redirectURLsRef, i)));
149    }
150
151    CFArrayRef dailyCounts = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionaryRef, dailyVisitCountKey));
152    if (dailyCounts && CFGetTypeID(dailyCounts) != CFArrayGetTypeID())
153        dailyCounts = 0;
154    CFArrayRef weeklyCounts = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionaryRef, weeklyVisitCountKey));
155    if (weeklyCounts && CFGetTypeID(weeklyCounts) != CFArrayGetTypeID())
156        weeklyCounts = 0;
157
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));
164
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;
172        }
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;
179        }
180    }
181
182    historyItemWrappers().remove(m_historyItem.get());
183    m_historyItem = HistoryItem::create(urlStringRef, titleRef, lastVisitedTime);
184    historyItemWrappers().set(m_historyItem.get(), this);
185
186    m_historyItem->setVisitCount(visitedCount);
187    if (lastVisitWasFailure)
188        m_historyItem->setLastVisitWasFailure(true);
189
190    if (lastVisitWasHTTPNonGet && (protocolIs(m_historyItem->urlString(), "http") || protocolIs(m_historyItem->urlString(), "https")))
191        m_historyItem->setLastVisitWasHTTPNonGet(true);
192
193    if (redirectURLsVector)
194        m_historyItem->setRedirectURLs(redirectURLsVector.release());
195
196    if (dailyVector.get())
197        m_historyItem->adoptVisitCounts(*dailyVector, *weeklyVector);
198
199    return S_OK;
200}
201
202HRESULT STDMETHODCALLTYPE WebHistoryItem::dictionaryRepresentation(void** dictionary)
203{
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)
209        return E_FAIL;
210
211    int keyCount = 0;
212    CFTypeRef keys[9];
213    CFTypeRef values[9];
214
215    if (!m_historyItem->urlString().isEmpty()) {
216        keys[keyCount] = urlKey;
217        values[keyCount++] = m_historyItem->urlString().createCFString();
218    }
219
220    keys[keyCount] = lastVisitedDateKey;
221    values[keyCount++] = lastVisitedStringRef;
222
223    if (!m_historyItem->title().isEmpty()) {
224        keys[keyCount] = titleKey;
225        values[keyCount++] = m_historyItem->title().createCFString();
226    }
227
228    keys[keyCount] = visitCountKey;
229    int visitCount = m_historyItem->visitCount();
230    values[keyCount++] = CFNumberCreate(0, kCFNumberIntType, &visitCount);
231
232    if (m_historyItem->lastVisitWasFailure()) {
233        keys[keyCount] = lastVisitWasFailureKey;
234        values[keyCount++] = CFRetain(kCFBooleanTrue);
235    }
236
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);
241    }
242
243    if (Vector<String>* redirectURLs = m_historyItem->redirectURLs()) {
244        size_t size = redirectURLs->size();
245        ASSERT(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)
251            CFRelease(items[i]);
252        delete[] items;
253
254        keys[keyCount] = redirectURLsKey;
255        values[keyCount++] = result;
256    }
257
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]);
263
264        CFArrayRef result = CFArrayCreate(0, (const void**)numbers.data(), size, &kCFTypeArrayCallBacks);
265
266        for (size_t i = 0; i < size; ++i)
267            CFRelease(numbers[i]);
268
269        keys[keyCount] = dailyVisitCountKey;
270        values[keyCount++] = result;
271    }
272
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]);
278
279        CFArrayRef result = CFArrayCreate(0, (const void**)numbers.data(), size, &kCFTypeArrayCallBacks);
280
281        for (size_t i = 0; i < size; ++i)
282            CFRelease(numbers[i]);
283
284        keys[keyCount] = weeklyVisitCountKey;
285        values[keyCount++] = result;
286    }
287
288    *dictionaryRef = CFDictionaryCreate(0, keys, values, keyCount, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
289
290    for (int i = 0; i < keyCount; ++i)
291        CFRelease(values[i]);
292
293    return S_OK;
294}
295
296HRESULT STDMETHODCALLTYPE WebHistoryItem::hasURLString(BOOL *hasURL)
297{
298    *hasURL = m_historyItem->urlString().isEmpty() ? FALSE : TRUE;
299    return S_OK;
300}
301
302HRESULT STDMETHODCALLTYPE WebHistoryItem::visitCount(int *count)
303{
304    *count = m_historyItem->visitCount();
305    return S_OK;
306}
307
308HRESULT STDMETHODCALLTYPE WebHistoryItem::setVisitCount(int count)
309{
310    m_historyItem->setVisitCount(count);
311    return S_OK;
312}
313
314HRESULT STDMETHODCALLTYPE WebHistoryItem::mergeAutoCompleteHints(IWebHistoryItem* otherItem)
315{
316    if (!otherItem)
317        return E_FAIL;
318
319    COMPtr<WebHistoryItem> otherWebHistoryItem(Query, otherItem);
320    if (!otherWebHistoryItem)
321        return E_FAIL;
322
323    m_historyItem->mergeAutoCompleteHints(otherWebHistoryItem->historyItem());
324
325    return S_OK;
326}
327
328HRESULT STDMETHODCALLTYPE WebHistoryItem::setLastVisitedTimeInterval(DATE time)
329{
330    m_historyItem->setLastVisitedTime(MarshallingHelpers::DATEToCFAbsoluteTime(time));
331    return S_OK;
332}
333
334HRESULT STDMETHODCALLTYPE WebHistoryItem::setTitle(BSTR title)
335{
336    m_historyItem->setTitle(String(title, SysStringLen(title)));
337
338    return S_OK;
339}
340
341HRESULT STDMETHODCALLTYPE WebHistoryItem::RSSFeedReferrer(BSTR* url)
342{
343    BString str(m_historyItem->referrer());
344    *url = str.release();
345
346    return S_OK;
347}
348
349HRESULT STDMETHODCALLTYPE WebHistoryItem::setRSSFeedReferrer(BSTR url)
350{
351    m_historyItem->setReferrer(String(url, SysStringLen(url)));
352
353    return S_OK;
354}
355
356HRESULT STDMETHODCALLTYPE WebHistoryItem::hasPageCache(BOOL* /*hasCache*/)
357{
358    // FIXME - TODO
359    ASSERT_NOT_REACHED();
360    return E_NOTIMPL;
361}
362
363HRESULT STDMETHODCALLTYPE WebHistoryItem::setHasPageCache(BOOL /*hasCache*/)
364{
365    // FIXME - TODO
366    return E_NOTIMPL;
367}
368
369HRESULT STDMETHODCALLTYPE WebHistoryItem::target(BSTR* target)
370{
371    if (!target) {
372        ASSERT_NOT_REACHED();
373        return E_POINTER;
374    }
375
376    *target = BString(m_historyItem->target()).release();
377    return S_OK;
378}
379
380HRESULT STDMETHODCALLTYPE WebHistoryItem::isTargetItem(BOOL* result)
381{
382    if (!result) {
383        ASSERT_NOT_REACHED();
384        return E_POINTER;
385    }
386
387    *result = m_historyItem->isTargetItem() ? TRUE : FALSE;
388    return S_OK;
389}
390
391HRESULT STDMETHODCALLTYPE WebHistoryItem::children(unsigned* outChildCount, SAFEARRAY** outChildren)
392{
393    if (!outChildCount || !outChildren) {
394        ASSERT_NOT_REACHED();
395        return E_POINTER;
396    }
397
398    *outChildCount = 0;
399    *outChildren = 0;
400
401    const HistoryItemVector& coreChildren = m_historyItem->children();
402    if (coreChildren.isEmpty())
403        return S_OK;
404    size_t childCount = coreChildren.size();
405
406    SAFEARRAY* children = SafeArrayCreateVector(VT_UNKNOWN, 0, static_cast<ULONG>(childCount));
407    if (!children)
408        return E_OUTOFMEMORY;
409
410    for (unsigned i = 0; i < childCount; ++i) {
411        COMPtr<WebHistoryItem> item(AdoptCOM, WebHistoryItem::createInstance(coreChildren[i]));
412        if (!item) {
413            SafeArrayDestroy(children);
414            return E_OUTOFMEMORY;
415        }
416
417        LONG longI = i;
418        HRESULT hr = SafeArrayPutElement(children, &longI, item.get());
419        if (FAILED(hr)) {
420            SafeArrayDestroy(children);
421            return hr;
422        }
423    }
424
425    *outChildCount = static_cast<unsigned>(childCount);
426    *outChildren = children;
427    return S_OK;
428
429}
430
431HRESULT STDMETHODCALLTYPE WebHistoryItem::lastVisitWasFailure(BOOL* wasFailure)
432{
433    if (!wasFailure) {
434        ASSERT_NOT_REACHED();
435        return E_POINTER;
436    }
437
438    *wasFailure = m_historyItem->lastVisitWasFailure();
439    return S_OK;
440}
441
442HRESULT STDMETHODCALLTYPE WebHistoryItem::setLastVisitWasFailure(BOOL wasFailure)
443{
444    m_historyItem->setLastVisitWasFailure(wasFailure);
445    return S_OK;
446}
447
448HRESULT STDMETHODCALLTYPE WebHistoryItem::lastVisitWasHTTPNonGet(BOOL* HTTPNonGet)
449{
450    if (!HTTPNonGet) {
451        ASSERT_NOT_REACHED();
452        return E_POINTER;
453    }
454
455    *HTTPNonGet = m_historyItem->lastVisitWasHTTPNonGet();
456
457    return S_OK;
458}
459
460HRESULT STDMETHODCALLTYPE WebHistoryItem::setLastVisitWasHTTPNonGet(BOOL HTTPNonGet)
461{
462    m_historyItem->setLastVisitWasHTTPNonGet(HTTPNonGet);
463    return S_OK;
464}
465
466HRESULT STDMETHODCALLTYPE WebHistoryItem::redirectURLs(IEnumVARIANT** urls)
467{
468    if (!urls) {
469        ASSERT_NOT_REACHED();
470        return E_POINTER;
471    }
472
473    Vector<String>* urlVector = m_historyItem->redirectURLs();
474    if (!urlVector) {
475        *urls = 0;
476        return S_OK;
477    }
478
479    COMPtr<COMEnumVariant<Vector<String> > > enumVariant(AdoptCOM, COMEnumVariant<Vector<String> >::createInstance(*urlVector));
480    *urls = enumVariant.releaseRef();
481
482    return S_OK;
483}
484
485HRESULT STDMETHODCALLTYPE WebHistoryItem::visitedWithTitle(BSTR title, BOOL increaseVisitCount)
486{
487    m_historyItem->visited(title, CFAbsoluteTimeGetCurrent(), increaseVisitCount ? IncreaseVisitCount : DoNotIncreaseVisitCount);
488    return S_OK;
489}
490
491HRESULT STDMETHODCALLTYPE WebHistoryItem::getDailyVisitCounts(int* number, int** counts)
492{
493    if (!number || !counts) {
494        ASSERT_NOT_REACHED();
495        return E_POINTER;
496    }
497
498    *counts = const_cast<int*>(m_historyItem->dailyVisitCounts().data());
499    *number = m_historyItem->dailyVisitCounts().size();
500    return S_OK;
501}
502
503HRESULT STDMETHODCALLTYPE WebHistoryItem::getWeeklyVisitCounts(int* number, int** counts)
504{
505    if (!number || !counts) {
506        ASSERT_NOT_REACHED();
507        return E_POINTER;
508    }
509
510    *counts = const_cast<int*>(m_historyItem->weeklyVisitCounts().data());
511    *number = m_historyItem->weeklyVisitCounts().size();
512    return S_OK;
513}
514
515HRESULT STDMETHODCALLTYPE WebHistoryItem::recordInitialVisit()
516{
517    m_historyItem->recordInitialVisit();
518    return S_OK;
519}
520
521// IUnknown -------------------------------------------------------------------
522
523HRESULT STDMETHODCALLTYPE WebHistoryItem::QueryInterface(REFIID riid, void** ppvObject)
524{
525    *ppvObject = 0;
526    if (IsEqualGUID(riid, __uuidof(WebHistoryItem)))
527        *ppvObject = this;
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);
534    else
535        return E_NOINTERFACE;
536
537    AddRef();
538    return S_OK;
539}
540
541ULONG STDMETHODCALLTYPE WebHistoryItem::AddRef(void)
542{
543    return ++m_refCount;
544}
545
546ULONG STDMETHODCALLTYPE WebHistoryItem::Release(void)
547{
548    ULONG newRef = --m_refCount;
549    if (!newRef)
550        delete(this);
551
552    return newRef;
553}
554
555// IWebHistoryItem -------------------------------------------------------------
556
557HRESULT STDMETHODCALLTYPE WebHistoryItem::initWithURLString(
558    /* [in] */ BSTR urlString,
559    /* [in] */ BSTR title,
560    /* [in] */ DATE lastVisited)
561{
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);
565
566    return S_OK;
567}
568
569HRESULT STDMETHODCALLTYPE WebHistoryItem::originalURLString(
570    /* [retval][out] */ BSTR* url)
571{
572    if (!url)
573        return E_POINTER;
574
575    BString str = m_historyItem->originalURLString();
576    *url = str.release();
577    return S_OK;
578}
579
580HRESULT STDMETHODCALLTYPE WebHistoryItem::URLString(
581    /* [retval][out] */ BSTR* url)
582{
583    if (!url)
584        return E_POINTER;
585
586    BString str = m_historyItem->urlString();
587    *url = str.release();
588    return S_OK;
589}
590
591HRESULT STDMETHODCALLTYPE WebHistoryItem::title(
592    /* [retval][out] */ BSTR* pageTitle)
593{
594    if (!pageTitle)
595        return E_POINTER;
596
597    BString str(m_historyItem->title());
598    *pageTitle = str.release();
599    return S_OK;
600}
601
602HRESULT STDMETHODCALLTYPE WebHistoryItem::lastVisitedTimeInterval(
603    /* [retval][out] */ DATE* lastVisited)
604{
605    if (!lastVisited)
606        return E_POINTER;
607
608    *lastVisited = MarshallingHelpers::CFAbsoluteTimeToDATE(m_historyItem->lastVisitedTime());
609    return S_OK;
610}
611
612HRESULT STDMETHODCALLTYPE WebHistoryItem::setAlternateTitle(
613    /* [in] */ BSTR title)
614{
615    m_alternateTitle = String(title, SysStringLen(title));
616    return S_OK;
617}
618
619HRESULT STDMETHODCALLTYPE WebHistoryItem::alternateTitle(
620    /* [retval][out] */ BSTR* title)
621{
622    if (!title) {
623        ASSERT_NOT_REACHED();
624        return E_POINTER;
625    }
626
627    *title = BString(m_alternateTitle).release();
628    return S_OK;
629}
630
631HRESULT STDMETHODCALLTYPE WebHistoryItem::icon(
632    /* [out, retval] */ OLE_HANDLE* /*hBitmap*/)
633{
634    ASSERT_NOT_REACHED();
635    return E_NOTIMPL;
636}
637
638// WebHistoryItem -------------------------------------------------------------
639
640HistoryItem* WebHistoryItem::historyItem() const
641{
642    return m_historyItem.get();
643}
644