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