Home | History | Annotate | Download | only in history
      1 /*
      2  * Copyright (C) 2005, 2006, 2008, 2011 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 "core/history/HistoryItem.h"
     28 
     29 #include <stdio.h>
     30 #include "bindings/v8/SerializedScriptValue.h"
     31 #include "core/dom/Document.h"
     32 #include "core/platform/network/ResourceRequest.h"
     33 #include "wtf/CurrentTime.h"
     34 #include "wtf/text/CString.h"
     35 
     36 namespace WebCore {
     37 
     38 static long long generateSequenceNumber()
     39 {
     40     // Initialize to the current time to reduce the likelihood of generating
     41     // identifiers that overlap with those from past/future browser sessions.
     42     static long long next = static_cast<long long>(currentTime() * 1000000.0);
     43     return ++next;
     44 }
     45 
     46 HistoryItem::HistoryItem()
     47     : m_lastVisitedTime(0)
     48     , m_pageScaleFactor(0)
     49     , m_isTargetItem(false)
     50     , m_visitCount(0)
     51     , m_itemSequenceNumber(generateSequenceNumber())
     52     , m_documentSequenceNumber(generateSequenceNumber())
     53 {
     54 }
     55 
     56 HistoryItem::HistoryItem(const String& urlString)
     57     : m_urlString(urlString)
     58     , m_originalURLString(urlString)
     59     , m_lastVisitedTime(0)
     60     , m_pageScaleFactor(0)
     61     , m_isTargetItem(false)
     62     , m_visitCount(0)
     63     , m_itemSequenceNumber(generateSequenceNumber())
     64     , m_documentSequenceNumber(generateSequenceNumber())
     65 {
     66 }
     67 
     68 HistoryItem::~HistoryItem()
     69 {
     70 }
     71 
     72 inline HistoryItem::HistoryItem(const HistoryItem& item)
     73     : RefCounted<HistoryItem>()
     74     , m_urlString(item.m_urlString)
     75     , m_originalURLString(item.m_originalURLString)
     76     , m_referrer(item.m_referrer)
     77     , m_target(item.m_target)
     78     , m_parent(item.m_parent)
     79     , m_title(item.m_title)
     80     , m_displayTitle(item.m_displayTitle)
     81     , m_lastVisitedTime(item.m_lastVisitedTime)
     82     , m_scrollPoint(item.m_scrollPoint)
     83     , m_pageScaleFactor(item.m_pageScaleFactor)
     84     , m_isTargetItem(item.m_isTargetItem)
     85     , m_visitCount(item.m_visitCount)
     86     , m_itemSequenceNumber(item.m_itemSequenceNumber)
     87     , m_documentSequenceNumber(item.m_documentSequenceNumber)
     88     , m_formContentType(item.m_formContentType)
     89 {
     90     if (item.m_formData)
     91         m_formData = item.m_formData->copy();
     92 
     93     unsigned size = item.m_children.size();
     94     m_children.reserveInitialCapacity(size);
     95     for (unsigned i = 0; i < size; ++i)
     96         m_children.uncheckedAppend(item.m_children[i]->copy());
     97 }
     98 
     99 PassRefPtr<HistoryItem> HistoryItem::copy() const
    100 {
    101     return adoptRef(new HistoryItem(*this));
    102 }
    103 
    104 void HistoryItem::reset()
    105 {
    106     m_urlString = String();
    107     m_originalURLString = String();
    108     m_referrer = String();
    109     m_target = String();
    110     m_parent = String();
    111     m_title = String();
    112     m_displayTitle = String();
    113 
    114     m_lastVisitedTime = 0;
    115 
    116     m_isTargetItem = false;
    117     m_visitCount = 0;
    118 
    119     m_itemSequenceNumber = generateSequenceNumber();
    120 
    121     m_stateObject = 0;
    122     m_documentSequenceNumber = generateSequenceNumber();
    123 
    124     m_formData = 0;
    125     m_formContentType = String();
    126 
    127     clearChildren();
    128 }
    129 
    130 const String& HistoryItem::urlString() const
    131 {
    132     return m_urlString;
    133 }
    134 
    135 // The first URL we loaded to get to where this history item points.  Includes both client
    136 // and server redirects.
    137 const String& HistoryItem::originalURLString() const
    138 {
    139     return m_originalURLString;
    140 }
    141 
    142 const String& HistoryItem::title() const
    143 {
    144     return m_title;
    145 }
    146 
    147 const String& HistoryItem::alternateTitle() const
    148 {
    149     return m_displayTitle;
    150 }
    151 
    152 double HistoryItem::lastVisitedTime() const
    153 {
    154     return m_lastVisitedTime;
    155 }
    156 
    157 KURL HistoryItem::url() const
    158 {
    159     return KURL(ParsedURLString, m_urlString);
    160 }
    161 
    162 KURL HistoryItem::originalURL() const
    163 {
    164     return KURL(ParsedURLString, m_originalURLString);
    165 }
    166 
    167 const String& HistoryItem::referrer() const
    168 {
    169     return m_referrer;
    170 }
    171 
    172 const String& HistoryItem::target() const
    173 {
    174     return m_target;
    175 }
    176 
    177 const String& HistoryItem::parent() const
    178 {
    179     return m_parent;
    180 }
    181 
    182 void HistoryItem::setAlternateTitle(const String& alternateTitle)
    183 {
    184     m_displayTitle = alternateTitle;
    185 }
    186 
    187 void HistoryItem::setURLString(const String& urlString)
    188 {
    189     if (m_urlString != urlString)
    190         m_urlString = urlString;
    191 }
    192 
    193 void HistoryItem::setURL(const KURL& url)
    194 {
    195     setURLString(url.string());
    196     clearDocumentState();
    197 }
    198 
    199 void HistoryItem::setOriginalURLString(const String& urlString)
    200 {
    201     m_originalURLString = urlString;
    202 }
    203 
    204 void HistoryItem::setReferrer(const String& referrer)
    205 {
    206     m_referrer = referrer;
    207 }
    208 
    209 void HistoryItem::setTitle(const String& title)
    210 {
    211     m_title = title;
    212 }
    213 
    214 void HistoryItem::setTarget(const String& target)
    215 {
    216     m_target = target;
    217 }
    218 
    219 void HistoryItem::setParent(const String& parent)
    220 {
    221     m_parent = parent;
    222 }
    223 
    224 void HistoryItem::recordVisitAtTime(double time)
    225 {
    226     m_lastVisitedTime = time;
    227     ++m_visitCount;
    228 }
    229 
    230 void HistoryItem::setLastVisitedTime(double time)
    231 {
    232     if (m_lastVisitedTime != time)
    233         recordVisitAtTime(time);
    234 }
    235 
    236 int HistoryItem::visitCount() const
    237 {
    238     return m_visitCount;
    239 }
    240 
    241 void HistoryItem::setVisitCount(int count)
    242 {
    243     m_visitCount = count;
    244 }
    245 
    246 const IntPoint& HistoryItem::scrollPoint() const
    247 {
    248     return m_scrollPoint;
    249 }
    250 
    251 void HistoryItem::setScrollPoint(const IntPoint& point)
    252 {
    253     m_scrollPoint = point;
    254 }
    255 
    256 void HistoryItem::clearScrollPoint()
    257 {
    258     m_scrollPoint.setX(0);
    259     m_scrollPoint.setY(0);
    260 }
    261 
    262 float HistoryItem::pageScaleFactor() const
    263 {
    264     return m_pageScaleFactor;
    265 }
    266 
    267 void HistoryItem::setPageScaleFactor(float scaleFactor)
    268 {
    269     m_pageScaleFactor = scaleFactor;
    270 }
    271 
    272 void HistoryItem::setDocumentState(const Vector<String>& state)
    273 {
    274     m_documentState = state;
    275 }
    276 
    277 const Vector<String>& HistoryItem::documentState() const
    278 {
    279     return m_documentState;
    280 }
    281 
    282 void HistoryItem::clearDocumentState()
    283 {
    284     m_documentState.clear();
    285 }
    286 
    287 bool HistoryItem::isTargetItem() const
    288 {
    289     return m_isTargetItem;
    290 }
    291 
    292 void HistoryItem::setIsTargetItem(bool flag)
    293 {
    294     m_isTargetItem = flag;
    295 }
    296 
    297 void HistoryItem::setStateObject(PassRefPtr<SerializedScriptValue> object)
    298 {
    299     m_stateObject = object;
    300 }
    301 
    302 void HistoryItem::addChildItem(PassRefPtr<HistoryItem> child)
    303 {
    304     ASSERT(!childItemWithTarget(child->target()));
    305     m_children.append(child);
    306 }
    307 
    308 void HistoryItem::setChildItem(PassRefPtr<HistoryItem> child)
    309 {
    310     ASSERT(!child->isTargetItem());
    311     unsigned size = m_children.size();
    312     for (unsigned i = 0; i < size; ++i)  {
    313         if (m_children[i]->target() == child->target()) {
    314             child->setIsTargetItem(m_children[i]->isTargetItem());
    315             m_children[i] = child;
    316             return;
    317         }
    318     }
    319     m_children.append(child);
    320 }
    321 
    322 HistoryItem* HistoryItem::childItemWithTarget(const String& target) const
    323 {
    324     unsigned size = m_children.size();
    325     for (unsigned i = 0; i < size; ++i) {
    326         if (m_children[i]->target() == target)
    327             return m_children[i].get();
    328     }
    329     return 0;
    330 }
    331 
    332 HistoryItem* HistoryItem::childItemWithDocumentSequenceNumber(long long number) const
    333 {
    334     unsigned size = m_children.size();
    335     for (unsigned i = 0; i < size; ++i) {
    336         if (m_children[i]->documentSequenceNumber() == number)
    337             return m_children[i].get();
    338     }
    339     return 0;
    340 }
    341 
    342 const HistoryItemVector& HistoryItem::children() const
    343 {
    344     return m_children;
    345 }
    346 
    347 void HistoryItem::clearChildren()
    348 {
    349     m_children.clear();
    350 }
    351 
    352 bool HistoryItem::isAncestorOf(const HistoryItem* item) const
    353 {
    354     for (size_t i = 0; i < m_children.size(); ++i) {
    355         HistoryItem* child = m_children[i].get();
    356         if (child == item)
    357             return true;
    358         if (child->isAncestorOf(item))
    359             return true;
    360     }
    361     return false;
    362 }
    363 
    364 // We do same-document navigation if going to a different item and if either of the following is true:
    365 // - The other item corresponds to the same document (for history entries created via pushState or fragment changes).
    366 // - The other item corresponds to the same set of documents, including frames (for history entries created via regular navigation)
    367 bool HistoryItem::shouldDoSameDocumentNavigationTo(HistoryItem* otherItem) const
    368 {
    369     if (this == otherItem)
    370         return false;
    371 
    372     if (stateObject() || otherItem->stateObject())
    373         return documentSequenceNumber() == otherItem->documentSequenceNumber();
    374 
    375     if ((url().hasFragmentIdentifier() || otherItem->url().hasFragmentIdentifier()) && equalIgnoringFragmentIdentifier(url(), otherItem->url()))
    376         return documentSequenceNumber() == otherItem->documentSequenceNumber();
    377 
    378     return hasSameDocumentTree(otherItem);
    379 }
    380 
    381 // Does a recursive check that this item and its descendants have the same
    382 // document sequence numbers as the other item.
    383 bool HistoryItem::hasSameDocumentTree(HistoryItem* otherItem) const
    384 {
    385     if (documentSequenceNumber() != otherItem->documentSequenceNumber())
    386         return false;
    387 
    388     if (children().size() != otherItem->children().size())
    389         return false;
    390 
    391     for (size_t i = 0; i < children().size(); i++) {
    392         HistoryItem* child = children()[i].get();
    393         HistoryItem* otherChild = otherItem->childItemWithDocumentSequenceNumber(child->documentSequenceNumber());
    394         if (!otherChild || !child->hasSameDocumentTree(otherChild))
    395             return false;
    396     }
    397 
    398     return true;
    399 }
    400 
    401 // Does a non-recursive check that this item and its immediate children have the
    402 // same frames as the other item.
    403 bool HistoryItem::hasSameFrames(HistoryItem* otherItem) const
    404 {
    405     if (target() != otherItem->target())
    406         return false;
    407 
    408     if (children().size() != otherItem->children().size())
    409         return false;
    410 
    411     for (size_t i = 0; i < children().size(); i++) {
    412         if (!otherItem->childItemWithTarget(children()[i]->target()))
    413             return false;
    414     }
    415 
    416     return true;
    417 }
    418 
    419 String HistoryItem::formContentType() const
    420 {
    421     return m_formContentType;
    422 }
    423 
    424 void HistoryItem::setFormInfoFromRequest(const ResourceRequest& request)
    425 {
    426     m_referrer = request.httpReferrer();
    427 
    428     if (equalIgnoringCase(request.httpMethod(), "POST")) {
    429         // FIXME: Eventually we have to make this smart enough to handle the case where
    430         // we have a stream for the body to handle the "data interspersed with files" feature.
    431         m_formData = request.httpBody();
    432         m_formContentType = request.httpContentType();
    433     } else {
    434         m_formData = 0;
    435         m_formContentType = String();
    436     }
    437 }
    438 
    439 void HistoryItem::setFormData(PassRefPtr<FormData> formData)
    440 {
    441     m_formData = formData;
    442 }
    443 
    444 void HistoryItem::setFormContentType(const String& formContentType)
    445 {
    446     m_formContentType = formContentType;
    447 }
    448 
    449 FormData* HistoryItem::formData()
    450 {
    451     return m_formData.get();
    452 }
    453 
    454 bool HistoryItem::isCurrentDocument(Document* doc) const
    455 {
    456     // FIXME: We should find a better way to check if this is the current document.
    457     return equalIgnoringFragmentIdentifier(url(), doc->url());
    458 }
    459 
    460 #ifndef NDEBUG
    461 
    462 int HistoryItem::showTree() const
    463 {
    464     return showTreeWithIndent(0);
    465 }
    466 
    467 int HistoryItem::showTreeWithIndent(unsigned indentLevel) const
    468 {
    469     Vector<char> prefix;
    470     for (unsigned i = 0; i < indentLevel; ++i)
    471         prefix.append("  ", 2);
    472     prefix.append("\0", 1);
    473 
    474     fprintf(stderr, "%s+-%s (%p)\n", prefix.data(), m_urlString.utf8().data(), this);
    475 
    476     int totalSubItems = 0;
    477     for (unsigned i = 0; i < m_children.size(); ++i)
    478         totalSubItems += m_children[i]->showTreeWithIndent(indentLevel + 1);
    479     return totalSubItems + 1;
    480 }
    481 
    482 #endif
    483 
    484 } // namespace WebCore
    485 
    486 #ifndef NDEBUG
    487 
    488 int showTree(const WebCore::HistoryItem* item)
    489 {
    490     return item->showTree();
    491 }
    492 
    493 #endif
    494