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 36namespace WebCore { 37 38static 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 46HistoryItem::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 56HistoryItem::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 68HistoryItem::~HistoryItem() 69{ 70} 71 72inline 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 99PassRefPtr<HistoryItem> HistoryItem::copy() const 100{ 101 return adoptRef(new HistoryItem(*this)); 102} 103 104void 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 130const 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. 137const String& HistoryItem::originalURLString() const 138{ 139 return m_originalURLString; 140} 141 142const String& HistoryItem::title() const 143{ 144 return m_title; 145} 146 147const String& HistoryItem::alternateTitle() const 148{ 149 return m_displayTitle; 150} 151 152double HistoryItem::lastVisitedTime() const 153{ 154 return m_lastVisitedTime; 155} 156 157KURL HistoryItem::url() const 158{ 159 return KURL(ParsedURLString, m_urlString); 160} 161 162KURL HistoryItem::originalURL() const 163{ 164 return KURL(ParsedURLString, m_originalURLString); 165} 166 167const String& HistoryItem::referrer() const 168{ 169 return m_referrer; 170} 171 172const String& HistoryItem::target() const 173{ 174 return m_target; 175} 176 177const String& HistoryItem::parent() const 178{ 179 return m_parent; 180} 181 182void HistoryItem::setAlternateTitle(const String& alternateTitle) 183{ 184 m_displayTitle = alternateTitle; 185} 186 187void HistoryItem::setURLString(const String& urlString) 188{ 189 if (m_urlString != urlString) 190 m_urlString = urlString; 191} 192 193void HistoryItem::setURL(const KURL& url) 194{ 195 setURLString(url.string()); 196 clearDocumentState(); 197} 198 199void HistoryItem::setOriginalURLString(const String& urlString) 200{ 201 m_originalURLString = urlString; 202} 203 204void HistoryItem::setReferrer(const String& referrer) 205{ 206 m_referrer = referrer; 207} 208 209void HistoryItem::setTitle(const String& title) 210{ 211 m_title = title; 212} 213 214void HistoryItem::setTarget(const String& target) 215{ 216 m_target = target; 217} 218 219void HistoryItem::setParent(const String& parent) 220{ 221 m_parent = parent; 222} 223 224void HistoryItem::recordVisitAtTime(double time) 225{ 226 m_lastVisitedTime = time; 227 ++m_visitCount; 228} 229 230void HistoryItem::setLastVisitedTime(double time) 231{ 232 if (m_lastVisitedTime != time) 233 recordVisitAtTime(time); 234} 235 236int HistoryItem::visitCount() const 237{ 238 return m_visitCount; 239} 240 241void HistoryItem::setVisitCount(int count) 242{ 243 m_visitCount = count; 244} 245 246const IntPoint& HistoryItem::scrollPoint() const 247{ 248 return m_scrollPoint; 249} 250 251void HistoryItem::setScrollPoint(const IntPoint& point) 252{ 253 m_scrollPoint = point; 254} 255 256void HistoryItem::clearScrollPoint() 257{ 258 m_scrollPoint.setX(0); 259 m_scrollPoint.setY(0); 260} 261 262float HistoryItem::pageScaleFactor() const 263{ 264 return m_pageScaleFactor; 265} 266 267void HistoryItem::setPageScaleFactor(float scaleFactor) 268{ 269 m_pageScaleFactor = scaleFactor; 270} 271 272void HistoryItem::setDocumentState(const Vector<String>& state) 273{ 274 m_documentState = state; 275} 276 277const Vector<String>& HistoryItem::documentState() const 278{ 279 return m_documentState; 280} 281 282void HistoryItem::clearDocumentState() 283{ 284 m_documentState.clear(); 285} 286 287bool HistoryItem::isTargetItem() const 288{ 289 return m_isTargetItem; 290} 291 292void HistoryItem::setIsTargetItem(bool flag) 293{ 294 m_isTargetItem = flag; 295} 296 297void HistoryItem::setStateObject(PassRefPtr<SerializedScriptValue> object) 298{ 299 m_stateObject = object; 300} 301 302void HistoryItem::addChildItem(PassRefPtr<HistoryItem> child) 303{ 304 ASSERT(!childItemWithTarget(child->target())); 305 m_children.append(child); 306} 307 308void 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 322HistoryItem* 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 332HistoryItem* 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 342const HistoryItemVector& HistoryItem::children() const 343{ 344 return m_children; 345} 346 347void HistoryItem::clearChildren() 348{ 349 m_children.clear(); 350} 351 352bool 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) 367bool 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. 383bool 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. 403bool 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 419String HistoryItem::formContentType() const 420{ 421 return m_formContentType; 422} 423 424void 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 439void HistoryItem::setFormData(PassRefPtr<FormData> formData) 440{ 441 m_formData = formData; 442} 443 444void HistoryItem::setFormContentType(const String& formContentType) 445{ 446 m_formContentType = formContentType; 447} 448 449FormData* HistoryItem::formData() 450{ 451 return m_formData.get(); 452} 453 454bool 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 462int HistoryItem::showTree() const 463{ 464 return showTreeWithIndent(0); 465} 466 467int 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 488int showTree(const WebCore::HistoryItem* item) 489{ 490 return item->showTree(); 491} 492 493#endif 494