2 * Copyright (C) 2005, 2006, 2008, 2011, 2014 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 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 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 "HistoryItem.h"
29 #include "CachedPage.h"
31 #include "IconDatabase.h"
32 #include "KeyedCoding.h"
33 #include "PageCache.h"
34 #include "ResourceRequest.h"
35 #include "SerializedScriptValue.h"
36 #include "SharedBuffer.h"
38 #include <wtf/CurrentTime.h>
39 #include <wtf/DateMath.h>
40 #include <wtf/text/CString.h>
44 static long long generateSequenceNumber()
46 // Initialize to the current time to reduce the likelihood of generating
47 // identifiers that overlap with those from past/future browser sessions.
48 static long long next = static_cast<long long>(currentTime() * 1000000.0);
52 static void defaultNotifyHistoryItemChanged(HistoryItem*)
56 WEBCORE_EXPORT void (*notifyHistoryItemChanged)(HistoryItem*) = defaultNotifyHistoryItemChanged;
58 HistoryItem::HistoryItem()
59 : m_pageScaleFactor(0)
60 , m_lastVisitWasFailure(false)
61 , m_isTargetItem(false)
62 , m_itemSequenceNumber(generateSequenceNumber())
63 , m_documentSequenceNumber(generateSequenceNumber())
66 , m_pruningReason(PruningReason::None)
69 , m_scaleIsInitial(false)
75 HistoryItem::HistoryItem(const String& urlString, const String& title)
76 : m_urlString(urlString)
77 , m_originalURLString(urlString)
79 , m_pageScaleFactor(0)
80 , m_lastVisitWasFailure(false)
81 , m_isTargetItem(false)
82 , m_itemSequenceNumber(generateSequenceNumber())
83 , m_documentSequenceNumber(generateSequenceNumber())
86 , m_pruningReason(PruningReason::None)
89 , m_scaleIsInitial(false)
93 iconDatabase().retainIconForPageURL(m_urlString);
96 HistoryItem::HistoryItem(const String& urlString, const String& title, const String& alternateTitle)
97 : m_urlString(urlString)
98 , m_originalURLString(urlString)
100 , m_displayTitle(alternateTitle)
101 , m_pageScaleFactor(0)
102 , m_lastVisitWasFailure(false)
103 , m_isTargetItem(false)
104 , m_itemSequenceNumber(generateSequenceNumber())
105 , m_documentSequenceNumber(generateSequenceNumber())
108 , m_pruningReason(PruningReason::None)
111 , m_scaleIsInitial(false)
115 iconDatabase().retainIconForPageURL(m_urlString);
118 HistoryItem::HistoryItem(const URL& url, const String& target, const String& parent, const String& title)
119 : m_urlString(url.string())
120 , m_originalURLString(url.string())
124 , m_pageScaleFactor(0)
125 , m_lastVisitWasFailure(false)
126 , m_isTargetItem(false)
127 , m_itemSequenceNumber(generateSequenceNumber())
128 , m_documentSequenceNumber(generateSequenceNumber())
131 , m_pruningReason(PruningReason::None)
134 , m_scaleIsInitial(false)
138 iconDatabase().retainIconForPageURL(m_urlString);
141 HistoryItem::~HistoryItem()
143 ASSERT(!m_cachedPage);
144 iconDatabase().releaseIconForPageURL(m_urlString);
147 inline HistoryItem::HistoryItem(const HistoryItem& item)
148 : RefCounted<HistoryItem>()
149 , m_urlString(item.m_urlString)
150 , m_originalURLString(item.m_originalURLString)
151 , m_referrer(item.m_referrer)
152 , m_target(item.m_target)
153 , m_parent(item.m_parent)
154 , m_title(item.m_title)
155 , m_displayTitle(item.m_displayTitle)
156 , m_scrollPoint(item.m_scrollPoint)
157 , m_pageScaleFactor(item.m_pageScaleFactor)
158 , m_lastVisitWasFailure(item.m_lastVisitWasFailure)
159 , m_isTargetItem(item.m_isTargetItem)
160 , m_itemSequenceNumber(item.m_itemSequenceNumber)
161 , m_documentSequenceNumber(item.m_documentSequenceNumber)
162 , m_formContentType(item.m_formContentType)
164 , m_scale(item.m_scale)
165 , m_scaleIsInitial(item.m_scaleIsInitial)
166 , m_bookmarkID(item.m_bookmarkID)
167 , m_sharedLinkUniqueIdentifier(item.m_sharedLinkUniqueIdentifier)
171 m_formData = item.m_formData->copy();
173 unsigned size = item.m_children.size();
174 m_children.reserveInitialCapacity(size);
175 for (unsigned i = 0; i < size; ++i)
176 m_children.uncheckedAppend(item.m_children[i]->copy());
178 if (item.m_redirectURLs)
179 m_redirectURLs = std::make_unique<Vector<String>>(*item.m_redirectURLs);
182 PassRefPtr<HistoryItem> HistoryItem::copy() const
184 return adoptRef(new HistoryItem(*this));
187 void HistoryItem::reset()
189 iconDatabase().releaseIconForPageURL(m_urlString);
191 m_urlString = String();
192 m_originalURLString = String();
193 m_referrer = String();
197 m_displayTitle = String();
199 m_lastVisitWasFailure = false;
200 m_isTargetItem = false;
202 m_redirectURLs = nullptr;
204 m_itemSequenceNumber = generateSequenceNumber();
207 m_documentSequenceNumber = generateSequenceNumber();
210 m_formContentType = String();
215 const String& HistoryItem::urlString() const
220 // The first URL we loaded to get to where this history item points. Includes both client
221 // and server redirects.
222 const String& HistoryItem::originalURLString() const
224 return m_originalURLString;
227 const String& HistoryItem::title() const
232 const String& HistoryItem::alternateTitle() const
234 return m_displayTitle;
237 bool HistoryItem::hasCachedPageExpired() const
239 return m_cachedPage ? m_cachedPage->hasExpired() : false;
242 URL HistoryItem::url() const
244 return URL(ParsedURLString, m_urlString);
247 URL HistoryItem::originalURL() const
249 return URL(ParsedURLString, m_originalURLString);
252 const String& HistoryItem::referrer() const
257 const String& HistoryItem::target() const
262 const String& HistoryItem::parent() const
267 void HistoryItem::setAlternateTitle(const String& alternateTitle)
269 m_displayTitle = alternateTitle;
270 notifyHistoryItemChanged(this);
273 void HistoryItem::setURLString(const String& urlString)
275 if (m_urlString != urlString) {
276 iconDatabase().releaseIconForPageURL(m_urlString);
277 m_urlString = urlString;
278 iconDatabase().retainIconForPageURL(m_urlString);
281 notifyHistoryItemChanged(this);
284 void HistoryItem::setURL(const URL& url)
286 pageCache()->remove(this);
287 setURLString(url.string());
288 clearDocumentState();
291 void HistoryItem::setOriginalURLString(const String& urlString)
293 m_originalURLString = urlString;
294 notifyHistoryItemChanged(this);
297 void HistoryItem::setReferrer(const String& referrer)
299 m_referrer = referrer;
300 notifyHistoryItemChanged(this);
303 void HistoryItem::setTitle(const String& title)
306 notifyHistoryItemChanged(this);
309 void HistoryItem::setTarget(const String& target)
312 notifyHistoryItemChanged(this);
315 void HistoryItem::setParent(const String& parent)
320 const IntPoint& HistoryItem::scrollPoint() const
322 return m_scrollPoint;
325 void HistoryItem::setScrollPoint(const IntPoint& point)
327 m_scrollPoint = point;
330 void HistoryItem::clearScrollPoint()
332 m_scrollPoint.setX(0);
333 m_scrollPoint.setY(0);
336 float HistoryItem::pageScaleFactor() const
338 return m_pageScaleFactor;
341 void HistoryItem::setPageScaleFactor(float scaleFactor)
343 m_pageScaleFactor = scaleFactor;
346 void HistoryItem::setDocumentState(const Vector<String>& state)
348 m_documentState = state;
351 const Vector<String>& HistoryItem::documentState() const
353 return m_documentState;
356 void HistoryItem::clearDocumentState()
358 m_documentState.clear();
361 bool HistoryItem::isTargetItem() const
363 return m_isTargetItem;
366 void HistoryItem::setIsTargetItem(bool flag)
368 m_isTargetItem = flag;
371 void HistoryItem::setStateObject(PassRefPtr<SerializedScriptValue> object)
373 m_stateObject = object;
376 void HistoryItem::addChildItem(PassRefPtr<HistoryItem> child)
378 ASSERT(!childItemWithTarget(child->target()));
379 m_children.append(child);
382 void HistoryItem::setChildItem(PassRefPtr<HistoryItem> child)
384 ASSERT(!child->isTargetItem());
385 unsigned size = m_children.size();
386 for (unsigned i = 0; i < size; ++i) {
387 if (m_children[i]->target() == child->target()) {
388 child->setIsTargetItem(m_children[i]->isTargetItem());
389 m_children[i] = child;
393 m_children.append(child);
396 HistoryItem* HistoryItem::childItemWithTarget(const String& target) const
398 unsigned size = m_children.size();
399 for (unsigned i = 0; i < size; ++i) {
400 if (m_children[i]->target() == target)
401 return m_children[i].get();
406 HistoryItem* HistoryItem::childItemWithDocumentSequenceNumber(long long number) const
408 unsigned size = m_children.size();
409 for (unsigned i = 0; i < size; ++i) {
410 if (m_children[i]->documentSequenceNumber() == number)
411 return m_children[i].get();
416 // <rdar://problem/4895849> HistoryItem::findTargetItem() should be replaced with a non-recursive method.
417 HistoryItem* HistoryItem::findTargetItem()
421 unsigned size = m_children.size();
422 for (unsigned i = 0; i < size; ++i) {
423 if (HistoryItem* match = m_children[i]->targetItem())
429 HistoryItem* HistoryItem::targetItem()
431 HistoryItem* foundItem = findTargetItem();
432 return foundItem ? foundItem : this;
435 const HistoryItemVector& HistoryItem::children() const
440 bool HistoryItem::hasChildren() const
442 return !m_children.isEmpty();
445 void HistoryItem::clearChildren()
450 bool HistoryItem::isAncestorOf(const HistoryItem* item) const
452 for (size_t i = 0; i < m_children.size(); ++i) {
453 HistoryItem* child = m_children[i].get();
456 if (child->isAncestorOf(item))
462 // We do same-document navigation if going to a different item and if either of the following is true:
463 // - The other item corresponds to the same document (for history entries created via pushState or fragment changes).
464 // - The other item corresponds to the same set of documents, including frames (for history entries created via regular navigation)
465 bool HistoryItem::shouldDoSameDocumentNavigationTo(HistoryItem* otherItem) const
467 if (this == otherItem)
470 if (stateObject() || otherItem->stateObject())
471 return documentSequenceNumber() == otherItem->documentSequenceNumber();
473 if ((url().hasFragmentIdentifier() || otherItem->url().hasFragmentIdentifier()) && equalIgnoringFragmentIdentifier(url(), otherItem->url()))
474 return documentSequenceNumber() == otherItem->documentSequenceNumber();
476 return hasSameDocumentTree(otherItem);
479 // Does a recursive check that this item and its descendants have the same
480 // document sequence numbers as the other item.
481 bool HistoryItem::hasSameDocumentTree(HistoryItem* otherItem) const
483 if (documentSequenceNumber() != otherItem->documentSequenceNumber())
486 if (children().size() != otherItem->children().size())
489 for (size_t i = 0; i < children().size(); i++) {
490 HistoryItem* child = children()[i].get();
491 HistoryItem* otherChild = otherItem->childItemWithDocumentSequenceNumber(child->documentSequenceNumber());
492 if (!otherChild || !child->hasSameDocumentTree(otherChild))
499 // Does a non-recursive check that this item and its immediate children have the
500 // same frames as the other item.
501 bool HistoryItem::hasSameFrames(HistoryItem* otherItem) const
503 if (target() != otherItem->target())
506 if (children().size() != otherItem->children().size())
509 for (size_t i = 0; i < children().size(); i++) {
510 if (!otherItem->childItemWithTarget(children()[i]->target()))
517 String HistoryItem::formContentType() const
519 return m_formContentType;
522 void HistoryItem::setFormInfoFromRequest(const ResourceRequest& request)
524 m_referrer = request.httpReferrer();
526 if (equalIgnoringCase(request.httpMethod(), "POST")) {
527 // FIXME: Eventually we have to make this smart enough to handle the case where
528 // we have a stream for the body to handle the "data interspersed with files" feature.
529 m_formData = request.httpBody();
530 m_formContentType = request.httpContentType();
533 m_formContentType = String();
537 void HistoryItem::setFormData(PassRefPtr<FormData> formData)
539 m_formData = formData;
542 void HistoryItem::setFormContentType(const String& formContentType)
544 m_formContentType = formContentType;
547 FormData* HistoryItem::formData()
549 return m_formData.get();
552 bool HistoryItem::isCurrentDocument(Document* doc) const
554 // FIXME: We should find a better way to check if this is the current document.
555 return equalIgnoringFragmentIdentifier(url(), doc->url());
558 void HistoryItem::addRedirectURL(const String& url)
561 m_redirectURLs = std::make_unique<Vector<String>>();
563 // Our API allows us to store all the URLs in the redirect chain, but for
564 // now we only have a use for the final URL.
565 (*m_redirectURLs).resize(1);
566 (*m_redirectURLs)[0] = url;
569 Vector<String>* HistoryItem::redirectURLs() const
571 return m_redirectURLs.get();
574 void HistoryItem::setRedirectURLs(std::unique_ptr<Vector<String>> redirectURLs)
576 m_redirectURLs = WTF::move(redirectURLs);
581 int HistoryItem::showTree() const
583 return showTreeWithIndent(0);
586 int HistoryItem::showTreeWithIndent(unsigned indentLevel) const
589 for (unsigned i = 0; i < indentLevel; ++i)
590 prefix.append(" ", 2);
591 prefix.append("\0", 1);
593 fprintf(stderr, "%s+-%s (%p)\n", prefix.data(), m_urlString.utf8().data(), this);
595 int totalSubItems = 0;
596 for (unsigned i = 0; i < m_children.size(); ++i)
597 totalSubItems += m_children[i]->showTreeWithIndent(indentLevel + 1);
598 return totalSubItems + 1;
603 } // namespace WebCore
607 int showTree(const WebCore::HistoryItem* item)
609 return item->showTree();