diff options
author | Steve Block <steveblock@google.com> | 2011-05-13 06:44:40 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-05-13 06:44:40 -0700 |
commit | 08014c20784f3db5df3a89b73cce46037b77eb59 (patch) | |
tree | 47749210d31e19e6e2f64036fa8fae2ad693476f /Source/WebCore/history/HistoryItem.cpp | |
parent | 860220379e56aeb66424861ad602b07ee22b4055 (diff) | |
parent | 4c3661f7918f8b3f139f824efb7855bedccb4c94 (diff) | |
download | external_webkit-08014c20784f3db5df3a89b73cce46037b77eb59.zip external_webkit-08014c20784f3db5df3a89b73cce46037b77eb59.tar.gz external_webkit-08014c20784f3db5df3a89b73cce46037b77eb59.tar.bz2 |
Merge changes Ide388898,Ic49f367c,I1158a808,Iacb6ca5d,I2100dd3a,I5c1abe54,Ib0ef9902,I31dbc523,I570314b3
* changes:
Merge WebKit at r75315: Update WebKit version
Merge WebKit at r75315: Add FrameLoaderClient PageCache stubs
Merge WebKit at r75315: Stub out AXObjectCache::remove()
Merge WebKit at r75315: Fix ImageBuffer
Merge WebKit at r75315: Fix PluginData::initPlugins()
Merge WebKit at r75315: Fix conflicts
Merge WebKit at r75315: Fix Makefiles
Merge WebKit at r75315: Move Android-specific WebCore files to Source
Merge WebKit at r75315: Initial merge by git.
Diffstat (limited to 'Source/WebCore/history/HistoryItem.cpp')
-rw-r--r-- | Source/WebCore/history/HistoryItem.cpp | 847 |
1 files changed, 847 insertions, 0 deletions
diff --git a/Source/WebCore/history/HistoryItem.cpp b/Source/WebCore/history/HistoryItem.cpp new file mode 100644 index 0000000..0995913 --- /dev/null +++ b/Source/WebCore/history/HistoryItem.cpp @@ -0,0 +1,847 @@ +/* + * Copyright (C) 2005, 2006, 2008, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HistoryItem.h" + +#include "CachedPage.h" +#include "Document.h" +#include "IconDatabase.h" +#include "PageCache.h" +#include "ResourceRequest.h" +#include <stdio.h> +#include <wtf/CurrentTime.h> +#include <wtf/Decoder.h> +#include <wtf/Encoder.h> +#include <wtf/MathExtras.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +const uint32_t backForwardTreeEncodingVersion = 0; + +static long long generateSequenceNumber() +{ + // Initialize to the current time to reduce the likelihood of generating + // identifiers that overlap with those from past/future browser sessions. + static long long next = static_cast<long long>(currentTime() * 1000000.0); + return ++next; +} + +static void defaultNotifyHistoryItemChanged(HistoryItem*) +{ +} + +void (*notifyHistoryItemChanged)(HistoryItem*) = defaultNotifyHistoryItemChanged; + +HistoryItem::HistoryItem() + : m_lastVisitedTime(0) + , m_lastVisitWasHTTPNonGet(false) + , m_lastVisitWasFailure(false) + , m_isTargetItem(false) + , m_visitCount(0) + , m_itemSequenceNumber(generateSequenceNumber()) + , m_documentSequenceNumber(generateSequenceNumber()) +{ +} + +HistoryItem::HistoryItem(const String& urlString, const String& title, double time) + : m_urlString(urlString) + , m_originalURLString(urlString) + , m_title(title) + , m_lastVisitedTime(time) + , m_lastVisitWasHTTPNonGet(false) + , m_lastVisitWasFailure(false) + , m_isTargetItem(false) + , m_visitCount(0) + , m_itemSequenceNumber(generateSequenceNumber()) + , m_documentSequenceNumber(generateSequenceNumber()) +{ + iconDatabase()->retainIconForPageURL(m_urlString); +} + +HistoryItem::HistoryItem(const String& urlString, const String& title, const String& alternateTitle, double time) + : m_urlString(urlString) + , m_originalURLString(urlString) + , m_title(title) + , m_displayTitle(alternateTitle) + , m_lastVisitedTime(time) + , m_lastVisitWasHTTPNonGet(false) + , m_lastVisitWasFailure(false) + , m_isTargetItem(false) + , m_visitCount(0) + , m_itemSequenceNumber(generateSequenceNumber()) + , m_documentSequenceNumber(generateSequenceNumber()) +{ + iconDatabase()->retainIconForPageURL(m_urlString); +} + +HistoryItem::HistoryItem(const KURL& url, const String& target, const String& parent, const String& title) + : m_urlString(url.string()) + , m_originalURLString(url.string()) + , m_target(target) + , m_parent(parent) + , m_title(title) + , m_lastVisitedTime(0) + , m_lastVisitWasHTTPNonGet(false) + , m_lastVisitWasFailure(false) + , m_isTargetItem(false) + , m_visitCount(0) + , m_itemSequenceNumber(generateSequenceNumber()) + , m_documentSequenceNumber(generateSequenceNumber()) +{ + iconDatabase()->retainIconForPageURL(m_urlString); +} + +HistoryItem::~HistoryItem() +{ + ASSERT(!m_cachedPage); + iconDatabase()->releaseIconForPageURL(m_urlString); +#if PLATFORM(ANDROID) + if (m_bridge) + m_bridge->detachHistoryItem(); +#endif +} + +inline HistoryItem::HistoryItem(const HistoryItem& item) + : RefCounted<HistoryItem>() + , m_urlString(item.m_urlString) + , m_originalURLString(item.m_originalURLString) + , m_referrer(item.m_referrer) + , m_target(item.m_target) + , m_parent(item.m_parent) + , m_title(item.m_title) + , m_displayTitle(item.m_displayTitle) + , m_lastVisitedTime(item.m_lastVisitedTime) + , m_lastVisitWasHTTPNonGet(item.m_lastVisitWasHTTPNonGet) + , m_scrollPoint(item.m_scrollPoint) + , m_lastVisitWasFailure(item.m_lastVisitWasFailure) + , m_isTargetItem(item.m_isTargetItem) + , m_visitCount(item.m_visitCount) + , m_dailyVisitCounts(item.m_dailyVisitCounts) + , m_weeklyVisitCounts(item.m_weeklyVisitCounts) + , m_itemSequenceNumber(item.m_itemSequenceNumber) + , m_documentSequenceNumber(item.m_documentSequenceNumber) + , m_formContentType(item.m_formContentType) +{ + if (item.m_formData) + m_formData = item.m_formData->copy(); + + unsigned size = item.m_children.size(); + m_children.reserveInitialCapacity(size); + for (unsigned i = 0; i < size; ++i) + m_children.uncheckedAppend(item.m_children[i]->copy()); + + if (item.m_redirectURLs) + m_redirectURLs = adoptPtr(new Vector<String>(*item.m_redirectURLs)); +} + +PassRefPtr<HistoryItem> HistoryItem::copy() const +{ + return adoptRef(new HistoryItem(*this)); +} + +const String& HistoryItem::urlString() const +{ + return m_urlString; +} + +// The first URL we loaded to get to where this history item points. Includes both client +// and server redirects. +const String& HistoryItem::originalURLString() const +{ + return m_originalURLString; +} + +const String& HistoryItem::title() const +{ + return m_title; +} + +const String& HistoryItem::alternateTitle() const +{ + return m_displayTitle; +} + +Image* HistoryItem::icon() const +{ + Image* result = iconDatabase()->iconForPageURL(m_urlString, IntSize(16, 16)); + return result ? result : iconDatabase()->defaultIcon(IntSize(16, 16)); +} + +double HistoryItem::lastVisitedTime() const +{ + return m_lastVisitedTime; +} + +KURL HistoryItem::url() const +{ + return KURL(ParsedURLString, m_urlString); +} + +KURL HistoryItem::originalURL() const +{ + return KURL(ParsedURLString, m_originalURLString); +} + +const String& HistoryItem::referrer() const +{ + return m_referrer; +} + +const String& HistoryItem::target() const +{ + return m_target; +} + +const String& HistoryItem::parent() const +{ + return m_parent; +} + +void HistoryItem::setAlternateTitle(const String& alternateTitle) +{ + m_displayTitle = alternateTitle; + notifyHistoryItemChanged(this); +} + +void HistoryItem::setURLString(const String& urlString) +{ + if (m_urlString != urlString) { + iconDatabase()->releaseIconForPageURL(m_urlString); + m_urlString = urlString; + iconDatabase()->retainIconForPageURL(m_urlString); + } + + notifyHistoryItemChanged(this); +} + +void HistoryItem::setURL(const KURL& url) +{ + pageCache()->remove(this); + setURLString(url.string()); + clearDocumentState(); +} + +void HistoryItem::setOriginalURLString(const String& urlString) +{ + m_originalURLString = urlString; + notifyHistoryItemChanged(this); +} + +void HistoryItem::setReferrer(const String& referrer) +{ + m_referrer = referrer; + notifyHistoryItemChanged(this); +} + +void HistoryItem::setTitle(const String& title) +{ + m_title = title; + notifyHistoryItemChanged(this); +} + +void HistoryItem::setTarget(const String& target) +{ + m_target = target; + notifyHistoryItemChanged(this); +} + +void HistoryItem::setParent(const String& parent) +{ + m_parent = parent; +} + +static inline int timeToDay(double time) +{ + static const double secondsPerDay = 60 * 60 * 24; + return static_cast<int>(ceil(time / secondsPerDay)); +} + +void HistoryItem::padDailyCountsForNewVisit(double time) +{ + if (m_dailyVisitCounts.isEmpty()) + m_dailyVisitCounts.prepend(m_visitCount); + + int daysElapsed = timeToDay(time) - timeToDay(m_lastVisitedTime); + + if (daysElapsed < 0) + daysElapsed = 0; + + Vector<int> padding; + padding.fill(0, daysElapsed); + m_dailyVisitCounts.prepend(padding); +} + +static const size_t daysPerWeek = 7; +static const size_t maxDailyCounts = 2 * daysPerWeek - 1; +static const size_t maxWeeklyCounts = 5; + +void HistoryItem::collapseDailyVisitsToWeekly() +{ + while (m_dailyVisitCounts.size() > maxDailyCounts) { + int oldestWeekTotal = 0; + for (size_t i = 0; i < daysPerWeek; i++) + oldestWeekTotal += m_dailyVisitCounts[m_dailyVisitCounts.size() - daysPerWeek + i]; + m_dailyVisitCounts.shrink(m_dailyVisitCounts.size() - daysPerWeek); + m_weeklyVisitCounts.prepend(oldestWeekTotal); + } + + if (m_weeklyVisitCounts.size() > maxWeeklyCounts) + m_weeklyVisitCounts.shrink(maxWeeklyCounts); +} + +void HistoryItem::recordVisitAtTime(double time, VisitCountBehavior visitCountBehavior) +{ + padDailyCountsForNewVisit(time); + + m_lastVisitedTime = time; + + if (visitCountBehavior == IncreaseVisitCount) { + ++m_visitCount; + ++m_dailyVisitCounts[0]; + } + + collapseDailyVisitsToWeekly(); +} + +void HistoryItem::setLastVisitedTime(double time) +{ + if (m_lastVisitedTime != time) + recordVisitAtTime(time); +} + +void HistoryItem::visited(const String& title, double time, VisitCountBehavior visitCountBehavior) +{ + m_title = title; + recordVisitAtTime(time, visitCountBehavior); +} + +int HistoryItem::visitCount() const +{ + return m_visitCount; +} + +void HistoryItem::recordInitialVisit() +{ + ASSERT(!m_visitCount); + recordVisitAtTime(m_lastVisitedTime); +} + +void HistoryItem::setVisitCount(int count) +{ + m_visitCount = count; +} + +void HistoryItem::adoptVisitCounts(Vector<int>& dailyCounts, Vector<int>& weeklyCounts) +{ + m_dailyVisitCounts.clear(); + m_dailyVisitCounts.swap(dailyCounts); + m_weeklyVisitCounts.clear(); + m_weeklyVisitCounts.swap(weeklyCounts); +} + +const IntPoint& HistoryItem::scrollPoint() const +{ + return m_scrollPoint; +} + +void HistoryItem::setScrollPoint(const IntPoint& point) +{ + m_scrollPoint = point; +} + +void HistoryItem::clearScrollPoint() +{ + m_scrollPoint.setX(0); + m_scrollPoint.setY(0); +} + +void HistoryItem::setDocumentState(const Vector<String>& state) +{ + m_documentState = state; +#if PLATFORM(ANDROID) + notifyHistoryItemChanged(this); +#endif +} + +const Vector<String>& HistoryItem::documentState() const +{ + return m_documentState; +} + +void HistoryItem::clearDocumentState() +{ + m_documentState.clear(); +#if PLATFORM(ANDROID) + notifyHistoryItemChanged(this); +#endif +} + +bool HistoryItem::isTargetItem() const +{ + return m_isTargetItem; +} + +void HistoryItem::setIsTargetItem(bool flag) +{ + m_isTargetItem = flag; +#if PLATFORM(ANDROID) + notifyHistoryItemChanged(this); +#endif +} + +void HistoryItem::setStateObject(PassRefPtr<SerializedScriptValue> object) +{ + m_stateObject = object; +} + +void HistoryItem::addChildItem(PassRefPtr<HistoryItem> child) +{ + ASSERT(!childItemWithTarget(child->target())); + m_children.append(child); +#if PLATFORM(ANDROID) + notifyHistoryItemChanged(this); +#endif +} + +void HistoryItem::setChildItem(PassRefPtr<HistoryItem> child) +{ + ASSERT(!child->isTargetItem()); + unsigned size = m_children.size(); + for (unsigned i = 0; i < size; ++i) { + if (m_children[i]->target() == child->target()) { + child->setIsTargetItem(m_children[i]->isTargetItem()); + m_children[i] = child; + return; + } + } + m_children.append(child); +} + +HistoryItem* HistoryItem::childItemWithTarget(const String& target) const +{ + unsigned size = m_children.size(); + for (unsigned i = 0; i < size; ++i) { + if (m_children[i]->target() == target) + return m_children[i].get(); + } + return 0; +} + +HistoryItem* HistoryItem::childItemWithDocumentSequenceNumber(long long number) const +{ + unsigned size = m_children.size(); + for (unsigned i = 0; i < size; ++i) { + if (m_children[i]->documentSequenceNumber() == number) + return m_children[i].get(); + } + return 0; +} + +// <rdar://problem/4895849> HistoryItem::findTargetItem() should be replaced with a non-recursive method. +HistoryItem* HistoryItem::findTargetItem() +{ + if (m_isTargetItem) + return this; + unsigned size = m_children.size(); + for (unsigned i = 0; i < size; ++i) { + if (HistoryItem* match = m_children[i]->targetItem()) + return match; + } + return 0; +} + +HistoryItem* HistoryItem::targetItem() +{ + HistoryItem* foundItem = findTargetItem(); + return foundItem ? foundItem : this; +} + +const HistoryItemVector& HistoryItem::children() const +{ + return m_children; +} + +bool HistoryItem::hasChildren() const +{ + return !m_children.isEmpty(); +} + +void HistoryItem::clearChildren() +{ + m_children.clear(); +} + +// We do same-document navigation if going to a different item and if either of the following is true: +// - The other item corresponds to the same document (for history entries created via pushState or fragment changes). +// - The other item corresponds to the same set of documents, including frames (for history entries created via regular navigation) +bool HistoryItem::shouldDoSameDocumentNavigationTo(HistoryItem* otherItem) const +{ + if (this == otherItem) + return false; + + if (stateObject() || otherItem->stateObject()) + return documentSequenceNumber() == otherItem->documentSequenceNumber(); + + if ((url().hasFragmentIdentifier() || otherItem->url().hasFragmentIdentifier()) && equalIgnoringFragmentIdentifier(url(), otherItem->url())) + return documentSequenceNumber() == otherItem->documentSequenceNumber(); + + return hasSameDocumentTree(otherItem); +} + +// Does a recursive check that this item and its descendants have the same +// document sequence numbers as the other item. +bool HistoryItem::hasSameDocumentTree(HistoryItem* otherItem) const +{ + if (documentSequenceNumber() != otherItem->documentSequenceNumber()) + return false; + + if (children().size() != otherItem->children().size()) + return false; + + for (size_t i = 0; i < children().size(); i++) { + HistoryItem* child = children()[i].get(); + HistoryItem* otherChild = otherItem->childItemWithDocumentSequenceNumber(child->documentSequenceNumber()); + if (!otherChild || !child->hasSameDocumentTree(otherChild)) + return false; + } + + return true; +} + +// Does a non-recursive check that this item and its immediate children have the +// same frames as the other item. +bool HistoryItem::hasSameFrames(HistoryItem* otherItem) const +{ + if (target() != otherItem->target()) + return false; + + if (children().size() != otherItem->children().size()) + return false; + + for (size_t i = 0; i < children().size(); i++) { + if (!otherItem->childItemWithTarget(children()[i]->target())) + return false; + } + + return true; +} + +String HistoryItem::formContentType() const +{ + return m_formContentType; +} + +void HistoryItem::setFormInfoFromRequest(const ResourceRequest& request) +{ + m_referrer = request.httpReferrer(); + + if (equalIgnoringCase(request.httpMethod(), "POST")) { + // FIXME: Eventually we have to make this smart enough to handle the case where + // we have a stream for the body to handle the "data interspersed with files" feature. + m_formData = request.httpBody(); + m_formContentType = request.httpContentType(); + } else { + m_formData = 0; + m_formContentType = String(); + } +#if PLATFORM(ANDROID) + notifyHistoryItemChanged(this); +#endif +} + +void HistoryItem::setFormData(PassRefPtr<FormData> formData) +{ + m_formData = formData; +} + +void HistoryItem::setFormContentType(const String& formContentType) +{ + m_formContentType = formContentType; +} + +FormData* HistoryItem::formData() +{ + return m_formData.get(); +} + +bool HistoryItem::isCurrentDocument(Document* doc) const +{ + // FIXME: We should find a better way to check if this is the current document. + return equalIgnoringFragmentIdentifier(url(), doc->url()); +} + +void HistoryItem::mergeAutoCompleteHints(HistoryItem* otherItem) +{ + // FIXME: this is broken - we should be merging the daily counts + // somehow. but this is to support API that's not really used in + // practice so leave it broken for now. + ASSERT(otherItem); + if (otherItem != this) + m_visitCount += otherItem->m_visitCount; +} + +void HistoryItem::addRedirectURL(const String& url) +{ + if (!m_redirectURLs) + m_redirectURLs = adoptPtr(new Vector<String>); + + // Our API allows us to store all the URLs in the redirect chain, but for + // now we only have a use for the final URL. + (*m_redirectURLs).resize(1); + (*m_redirectURLs)[0] = url; +} + +Vector<String>* HistoryItem::redirectURLs() const +{ + return m_redirectURLs.get(); +} + +void HistoryItem::setRedirectURLs(PassOwnPtr<Vector<String> > redirectURLs) +{ + m_redirectURLs = redirectURLs; +} + +void HistoryItem::encodeBackForwardTree(Encoder& encoder) const +{ + encoder.encodeUInt32(backForwardTreeEncodingVersion); + + encodeBackForwardTreeNode(encoder); +} + +void HistoryItem::encodeBackForwardTreeNode(Encoder& encoder) const +{ + size_t size = m_children.size(); + encoder.encodeUInt64(size); + for (size_t i = 0; i < size; ++i) { + const HistoryItem& child = *m_children[i]; + + encoder.encodeString(child.m_originalURLString); + + encoder.encodeString(child.m_urlString); + + child.encodeBackForwardTreeNode(encoder); + } + + encoder.encodeInt64(m_documentSequenceNumber); + + size = m_documentState.size(); + encoder.encodeUInt64(size); + for (size_t i = 0; i < size; ++i) + encoder.encodeString(m_documentState[i]); + + encoder.encodeString(m_formContentType); + + encoder.encodeBool(m_formData); + if (m_formData) + m_formData->encodeForBackForward(encoder); + + encoder.encodeInt64(m_itemSequenceNumber); + + encoder.encodeString(m_originalURLString); + + encoder.encodeString(m_referrer); + + encoder.encodeInt32(m_scrollPoint.x()); + encoder.encodeInt32(m_scrollPoint.y()); + + encoder.encodeBool(m_stateObject); + if (m_stateObject) { +#if !USE(V8) + encoder.encodeBytes(m_stateObject->data().data(), m_stateObject->data().size()); +#else + encoder.encodeString(m_stateObject->toWireString()); +#endif + } + + encoder.encodeString(m_target); +} + +struct DecodeRecursionStackElement { + RefPtr<HistoryItem> node; + size_t i; + uint64_t size; + + DecodeRecursionStackElement(PassRefPtr<HistoryItem> node, size_t i, uint64_t size) + : node(node) + , i(i) + , size(size) + { + } +}; + +PassRefPtr<HistoryItem> HistoryItem::decodeBackForwardTree(const String& topURLString, const String& topTitle, const String& topOriginalURLString, Decoder& decoder) +{ + // Since the data stream is not trusted, the decode has to be non-recursive. + // We don't want bad data to cause a stack overflow. + + uint32_t version; + if (!decoder.decodeUInt32(version)) + return 0; + if (version != backForwardTreeEncodingVersion) + return 0; + + String urlString = topURLString; + String title = topTitle; + String originalURLString = topOriginalURLString; + + Vector<DecodeRecursionStackElement, 16> recursionStack; + +recurse: + RefPtr<HistoryItem> node = create(urlString, title, 0); + + node->setOriginalURLString(originalURLString); + + title = String(); + + uint64_t size; + if (!decoder.decodeUInt64(size)) + return 0; + size_t i; + RefPtr<HistoryItem> child; + for (i = 0; i < size; ++i) { + if (!decoder.decodeString(originalURLString)) + return 0; + + if (!decoder.decodeString(urlString)) + return 0; + + recursionStack.append(DecodeRecursionStackElement(node.release(), i, size)); + goto recurse; + +resume: + node->m_children.append(child.release()); + } + + if (!decoder.decodeInt64(node->m_documentSequenceNumber)) + return 0; + + if (!decoder.decodeUInt64(size)) + return 0; + for (i = 0; i < size; ++i) { + String state; + if (!decoder.decodeString(state)) + return 0; + node->m_documentState.append(state); + } + + if (!decoder.decodeString(node->m_formContentType)) + return 0; + + bool hasFormData; + if (!decoder.decodeBool(hasFormData)) + return 0; + if (hasFormData) { + node->m_formData = FormData::decodeForBackForward(decoder); + if (!node->m_formData) + return 0; + } + + if (!decoder.decodeInt64(node->m_itemSequenceNumber)) + return 0; + + if (!decoder.decodeString(node->m_originalURLString)) + return 0; + + if (!decoder.decodeString(node->m_referrer)) + return 0; + + int32_t x; + if (!decoder.decodeInt32(x)) + return 0; + int32_t y; + if (!decoder.decodeInt32(y)) + return 0; + node->m_scrollPoint = IntPoint(x, y); + + bool hasStateObject; + if (!decoder.decodeBool(hasStateObject)) + return 0; + if (hasStateObject) { +#if !USE(V8) + Vector<uint8_t> bytes; + if (!decoder.decodeBytes(bytes)) + return 0; + node->m_stateObject = SerializedScriptValue::adopt(bytes); +#else + String string; + if (!decoder.decodeString(string)) + return 0; + node->m_stateObject = SerializedScriptValue::createFromWire(string); +#endif + } + + if (!decoder.decodeString(node->m_target)) + return 0; + + // Simulate recursion with our own stack. + if (!recursionStack.isEmpty()) { + DecodeRecursionStackElement& element = recursionStack.last(); + child = node.release(); + node = element.node.release(); + i = element.i; + size = element.size; + recursionStack.removeLast(); + goto resume; + } + + return node.release(); +} + +#ifndef NDEBUG + +int HistoryItem::showTree() const +{ + return showTreeWithIndent(0); +} + +int HistoryItem::showTreeWithIndent(unsigned indentLevel) const +{ + Vector<char> prefix; + for (unsigned i = 0; i < indentLevel; ++i) + prefix.append(" ", 2); + prefix.append("\0", 1); + + fprintf(stderr, "%s+-%s (%p)\n", prefix.data(), m_urlString.utf8().data(), this); + + int totalSubItems = 0; + for (unsigned i = 0; i < m_children.size(); ++i) + totalSubItems += m_children[i]->showTreeWithIndent(indentLevel + 1); + return totalSubItems + 1; +} + +#endif + +} // namespace WebCore + +#ifndef NDEBUG + +int showTree(const WebCore::HistoryItem* item) +{ + return item->showTree(); +} + +#endif |