1/* 2 * Copyright (C) 2005, 2006, 2008, 2011, 2014 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 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. 24 */ 25 26#include "config.h" 27#include "HistoryItem.h" 28 29#include "CachedPage.h" 30#include "Document.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" 37#include <stdio.h> 38#include <wtf/CurrentTime.h> 39#include <wtf/DateMath.h> 40#include <wtf/text/CString.h> 41 42namespace WebCore { 43 44static long long generateSequenceNumber() 45{ 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); 49 return ++next; 50} 51 52static void defaultNotifyHistoryItemChanged(HistoryItem*) 53{ 54} 55 56void (*notifyHistoryItemChanged)(HistoryItem*) = defaultNotifyHistoryItemChanged; 57 58HistoryItem::HistoryItem() 59 : m_pageScaleFactor(0) 60 , m_lastVisitWasFailure(false) 61 , m_isTargetItem(false) 62 , m_itemSequenceNumber(generateSequenceNumber()) 63 , m_documentSequenceNumber(generateSequenceNumber()) 64 , m_next(0) 65 , m_prev(0) 66#if PLATFORM(IOS) 67 , m_scale(0) 68 , m_scaleIsInitial(false) 69 , m_bookmarkID(0) 70#endif 71{ 72} 73 74HistoryItem::HistoryItem(const String& urlString, const String& title) 75 : m_urlString(urlString) 76 , m_originalURLString(urlString) 77 , m_title(title) 78 , m_pageScaleFactor(0) 79 , m_lastVisitWasFailure(false) 80 , m_isTargetItem(false) 81 , m_itemSequenceNumber(generateSequenceNumber()) 82 , m_documentSequenceNumber(generateSequenceNumber()) 83 , m_next(0) 84 , m_prev(0) 85#if PLATFORM(IOS) 86 , m_scale(0) 87 , m_scaleIsInitial(false) 88 , m_bookmarkID(0) 89#endif 90{ 91 iconDatabase().retainIconForPageURL(m_urlString); 92} 93 94HistoryItem::HistoryItem(const String& urlString, const String& title, const String& alternateTitle) 95 : m_urlString(urlString) 96 , m_originalURLString(urlString) 97 , m_title(title) 98 , m_displayTitle(alternateTitle) 99 , m_pageScaleFactor(0) 100 , m_lastVisitWasFailure(false) 101 , m_isTargetItem(false) 102 , m_itemSequenceNumber(generateSequenceNumber()) 103 , m_documentSequenceNumber(generateSequenceNumber()) 104 , m_next(0) 105 , m_prev(0) 106#if PLATFORM(IOS) 107 , m_scale(0) 108 , m_scaleIsInitial(false) 109 , m_bookmarkID(0) 110#endif 111{ 112 iconDatabase().retainIconForPageURL(m_urlString); 113} 114 115HistoryItem::HistoryItem(const URL& url, const String& target, const String& parent, const String& title) 116 : m_urlString(url.string()) 117 , m_originalURLString(url.string()) 118 , m_target(target) 119 , m_parent(parent) 120 , m_title(title) 121 , m_pageScaleFactor(0) 122 , m_lastVisitWasFailure(false) 123 , m_isTargetItem(false) 124 , m_itemSequenceNumber(generateSequenceNumber()) 125 , m_documentSequenceNumber(generateSequenceNumber()) 126 , m_next(0) 127 , m_prev(0) 128#if PLATFORM(IOS) 129 , m_scale(0) 130 , m_scaleIsInitial(false) 131 , m_bookmarkID(0) 132#endif 133{ 134 iconDatabase().retainIconForPageURL(m_urlString); 135} 136 137HistoryItem::~HistoryItem() 138{ 139 ASSERT(!m_cachedPage); 140 iconDatabase().releaseIconForPageURL(m_urlString); 141} 142 143inline HistoryItem::HistoryItem(const HistoryItem& item) 144 : RefCounted<HistoryItem>() 145 , m_urlString(item.m_urlString) 146 , m_originalURLString(item.m_originalURLString) 147 , m_referrer(item.m_referrer) 148 , m_target(item.m_target) 149 , m_parent(item.m_parent) 150 , m_title(item.m_title) 151 , m_displayTitle(item.m_displayTitle) 152 , m_scrollPoint(item.m_scrollPoint) 153 , m_pageScaleFactor(item.m_pageScaleFactor) 154 , m_lastVisitWasFailure(item.m_lastVisitWasFailure) 155 , m_isTargetItem(item.m_isTargetItem) 156 , m_itemSequenceNumber(item.m_itemSequenceNumber) 157 , m_documentSequenceNumber(item.m_documentSequenceNumber) 158 , m_formContentType(item.m_formContentType) 159#if PLATFORM(IOS) 160 , m_scale(item.m_scale) 161 , m_scaleIsInitial(item.m_scaleIsInitial) 162 , m_bookmarkID(item.m_bookmarkID) 163 , m_sharedLinkUniqueIdentifier(item.m_sharedLinkUniqueIdentifier) 164#endif 165{ 166 if (item.m_formData) 167 m_formData = item.m_formData->copy(); 168 169 unsigned size = item.m_children.size(); 170 m_children.reserveInitialCapacity(size); 171 for (unsigned i = 0; i < size; ++i) 172 m_children.uncheckedAppend(item.m_children[i]->copy()); 173 174 if (item.m_redirectURLs) 175 m_redirectURLs = std::make_unique<Vector<String>>(*item.m_redirectURLs); 176} 177 178PassRefPtr<HistoryItem> HistoryItem::copy() const 179{ 180 return adoptRef(new HistoryItem(*this)); 181} 182 183void HistoryItem::reset() 184{ 185 iconDatabase().releaseIconForPageURL(m_urlString); 186 187 m_urlString = String(); 188 m_originalURLString = String(); 189 m_referrer = String(); 190 m_target = String(); 191 m_parent = String(); 192 m_title = String(); 193 m_displayTitle = String(); 194 195 m_lastVisitWasFailure = false; 196 m_isTargetItem = false; 197 198 m_redirectURLs = nullptr; 199 200 m_itemSequenceNumber = generateSequenceNumber(); 201 202 m_stateObject = 0; 203 m_documentSequenceNumber = generateSequenceNumber(); 204 205 m_formData = 0; 206 m_formContentType = String(); 207 208 clearChildren(); 209} 210 211const String& HistoryItem::urlString() const 212{ 213 return m_urlString; 214} 215 216// The first URL we loaded to get to where this history item points. Includes both client 217// and server redirects. 218const String& HistoryItem::originalURLString() const 219{ 220 return m_originalURLString; 221} 222 223const String& HistoryItem::title() const 224{ 225 return m_title; 226} 227 228const String& HistoryItem::alternateTitle() const 229{ 230 return m_displayTitle; 231} 232 233bool HistoryItem::hasCachedPageExpired() const 234{ 235 return m_cachedPage ? m_cachedPage->hasExpired() : false; 236} 237 238URL HistoryItem::url() const 239{ 240 return URL(ParsedURLString, m_urlString); 241} 242 243URL HistoryItem::originalURL() const 244{ 245 return URL(ParsedURLString, m_originalURLString); 246} 247 248const String& HistoryItem::referrer() const 249{ 250 return m_referrer; 251} 252 253const String& HistoryItem::target() const 254{ 255 return m_target; 256} 257 258const String& HistoryItem::parent() const 259{ 260 return m_parent; 261} 262 263void HistoryItem::setAlternateTitle(const String& alternateTitle) 264{ 265 m_displayTitle = alternateTitle; 266 notifyHistoryItemChanged(this); 267} 268 269void HistoryItem::setURLString(const String& urlString) 270{ 271 if (m_urlString != urlString) { 272 iconDatabase().releaseIconForPageURL(m_urlString); 273 m_urlString = urlString; 274 iconDatabase().retainIconForPageURL(m_urlString); 275 } 276 277 notifyHistoryItemChanged(this); 278} 279 280void HistoryItem::setURL(const URL& url) 281{ 282 pageCache()->remove(this); 283 setURLString(url.string()); 284 clearDocumentState(); 285} 286 287void HistoryItem::setOriginalURLString(const String& urlString) 288{ 289 m_originalURLString = urlString; 290 notifyHistoryItemChanged(this); 291} 292 293void HistoryItem::setReferrer(const String& referrer) 294{ 295 m_referrer = referrer; 296 notifyHistoryItemChanged(this); 297} 298 299void HistoryItem::setTitle(const String& title) 300{ 301 m_title = title; 302 notifyHistoryItemChanged(this); 303} 304 305void HistoryItem::setTarget(const String& target) 306{ 307 m_target = target; 308 notifyHistoryItemChanged(this); 309} 310 311void HistoryItem::setParent(const String& parent) 312{ 313 m_parent = parent; 314} 315 316const IntPoint& HistoryItem::scrollPoint() const 317{ 318 return m_scrollPoint; 319} 320 321void HistoryItem::setScrollPoint(const IntPoint& point) 322{ 323 m_scrollPoint = point; 324} 325 326void HistoryItem::clearScrollPoint() 327{ 328 m_scrollPoint.setX(0); 329 m_scrollPoint.setY(0); 330} 331 332float HistoryItem::pageScaleFactor() const 333{ 334 return m_pageScaleFactor; 335} 336 337void HistoryItem::setPageScaleFactor(float scaleFactor) 338{ 339 m_pageScaleFactor = scaleFactor; 340} 341 342void HistoryItem::setDocumentState(const Vector<String>& state) 343{ 344 m_documentState = state; 345} 346 347const Vector<String>& HistoryItem::documentState() const 348{ 349 return m_documentState; 350} 351 352void HistoryItem::clearDocumentState() 353{ 354 m_documentState.clear(); 355} 356 357bool HistoryItem::isTargetItem() const 358{ 359 return m_isTargetItem; 360} 361 362void HistoryItem::setIsTargetItem(bool flag) 363{ 364 m_isTargetItem = flag; 365} 366 367void HistoryItem::setStateObject(PassRefPtr<SerializedScriptValue> object) 368{ 369 m_stateObject = object; 370} 371 372void HistoryItem::addChildItem(PassRefPtr<HistoryItem> child) 373{ 374 ASSERT(!childItemWithTarget(child->target())); 375 m_children.append(child); 376} 377 378void HistoryItem::setChildItem(PassRefPtr<HistoryItem> child) 379{ 380 ASSERT(!child->isTargetItem()); 381 unsigned size = m_children.size(); 382 for (unsigned i = 0; i < size; ++i) { 383 if (m_children[i]->target() == child->target()) { 384 child->setIsTargetItem(m_children[i]->isTargetItem()); 385 m_children[i] = child; 386 return; 387 } 388 } 389 m_children.append(child); 390} 391 392HistoryItem* HistoryItem::childItemWithTarget(const String& target) const 393{ 394 unsigned size = m_children.size(); 395 for (unsigned i = 0; i < size; ++i) { 396 if (m_children[i]->target() == target) 397 return m_children[i].get(); 398 } 399 return 0; 400} 401 402HistoryItem* HistoryItem::childItemWithDocumentSequenceNumber(long long number) const 403{ 404 unsigned size = m_children.size(); 405 for (unsigned i = 0; i < size; ++i) { 406 if (m_children[i]->documentSequenceNumber() == number) 407 return m_children[i].get(); 408 } 409 return 0; 410} 411 412// <rdar://problem/4895849> HistoryItem::findTargetItem() should be replaced with a non-recursive method. 413HistoryItem* HistoryItem::findTargetItem() 414{ 415 if (m_isTargetItem) 416 return this; 417 unsigned size = m_children.size(); 418 for (unsigned i = 0; i < size; ++i) { 419 if (HistoryItem* match = m_children[i]->targetItem()) 420 return match; 421 } 422 return 0; 423} 424 425HistoryItem* HistoryItem::targetItem() 426{ 427 HistoryItem* foundItem = findTargetItem(); 428 return foundItem ? foundItem : this; 429} 430 431const HistoryItemVector& HistoryItem::children() const 432{ 433 return m_children; 434} 435 436bool HistoryItem::hasChildren() const 437{ 438 return !m_children.isEmpty(); 439} 440 441void HistoryItem::clearChildren() 442{ 443 m_children.clear(); 444} 445 446bool HistoryItem::isAncestorOf(const HistoryItem* item) const 447{ 448 for (size_t i = 0; i < m_children.size(); ++i) { 449 HistoryItem* child = m_children[i].get(); 450 if (child == item) 451 return true; 452 if (child->isAncestorOf(item)) 453 return true; 454 } 455 return false; 456} 457 458// We do same-document navigation if going to a different item and if either of the following is true: 459// - The other item corresponds to the same document (for history entries created via pushState or fragment changes). 460// - The other item corresponds to the same set of documents, including frames (for history entries created via regular navigation) 461bool HistoryItem::shouldDoSameDocumentNavigationTo(HistoryItem* otherItem) const 462{ 463 if (this == otherItem) 464 return false; 465 466 if (stateObject() || otherItem->stateObject()) 467 return documentSequenceNumber() == otherItem->documentSequenceNumber(); 468 469 if ((url().hasFragmentIdentifier() || otherItem->url().hasFragmentIdentifier()) && equalIgnoringFragmentIdentifier(url(), otherItem->url())) 470 return documentSequenceNumber() == otherItem->documentSequenceNumber(); 471 472 return hasSameDocumentTree(otherItem); 473} 474 475// Does a recursive check that this item and its descendants have the same 476// document sequence numbers as the other item. 477bool HistoryItem::hasSameDocumentTree(HistoryItem* otherItem) const 478{ 479 if (documentSequenceNumber() != otherItem->documentSequenceNumber()) 480 return false; 481 482 if (children().size() != otherItem->children().size()) 483 return false; 484 485 for (size_t i = 0; i < children().size(); i++) { 486 HistoryItem* child = children()[i].get(); 487 HistoryItem* otherChild = otherItem->childItemWithDocumentSequenceNumber(child->documentSequenceNumber()); 488 if (!otherChild || !child->hasSameDocumentTree(otherChild)) 489 return false; 490 } 491 492 return true; 493} 494 495// Does a non-recursive check that this item and its immediate children have the 496// same frames as the other item. 497bool HistoryItem::hasSameFrames(HistoryItem* otherItem) const 498{ 499 if (target() != otherItem->target()) 500 return false; 501 502 if (children().size() != otherItem->children().size()) 503 return false; 504 505 for (size_t i = 0; i < children().size(); i++) { 506 if (!otherItem->childItemWithTarget(children()[i]->target())) 507 return false; 508 } 509 510 return true; 511} 512 513String HistoryItem::formContentType() const 514{ 515 return m_formContentType; 516} 517 518void HistoryItem::setFormInfoFromRequest(const ResourceRequest& request) 519{ 520 m_referrer = request.httpReferrer(); 521 522 if (equalIgnoringCase(request.httpMethod(), "POST")) { 523 // FIXME: Eventually we have to make this smart enough to handle the case where 524 // we have a stream for the body to handle the "data interspersed with files" feature. 525 m_formData = request.httpBody(); 526 m_formContentType = request.httpContentType(); 527 } else { 528 m_formData = 0; 529 m_formContentType = String(); 530 } 531} 532 533void HistoryItem::setFormData(PassRefPtr<FormData> formData) 534{ 535 m_formData = formData; 536} 537 538void HistoryItem::setFormContentType(const String& formContentType) 539{ 540 m_formContentType = formContentType; 541} 542 543FormData* HistoryItem::formData() 544{ 545 return m_formData.get(); 546} 547 548bool HistoryItem::isCurrentDocument(Document* doc) const 549{ 550 // FIXME: We should find a better way to check if this is the current document. 551 return equalIgnoringFragmentIdentifier(url(), doc->url()); 552} 553 554void HistoryItem::addRedirectURL(const String& url) 555{ 556 if (!m_redirectURLs) 557 m_redirectURLs = std::make_unique<Vector<String>>(); 558 559 // Our API allows us to store all the URLs in the redirect chain, but for 560 // now we only have a use for the final URL. 561 (*m_redirectURLs).resize(1); 562 (*m_redirectURLs)[0] = url; 563} 564 565Vector<String>* HistoryItem::redirectURLs() const 566{ 567 return m_redirectURLs.get(); 568} 569 570void HistoryItem::setRedirectURLs(std::unique_ptr<Vector<String>> redirectURLs) 571{ 572 m_redirectURLs = WTF::move(redirectURLs); 573} 574 575#ifndef NDEBUG 576 577int HistoryItem::showTree() const 578{ 579 return showTreeWithIndent(0); 580} 581 582int HistoryItem::showTreeWithIndent(unsigned indentLevel) const 583{ 584 Vector<char> prefix; 585 for (unsigned i = 0; i < indentLevel; ++i) 586 prefix.append(" ", 2); 587 prefix.append("\0", 1); 588 589 fprintf(stderr, "%s+-%s (%p)\n", prefix.data(), m_urlString.utf8().data(), this); 590 591 int totalSubItems = 0; 592 for (unsigned i = 0; i < m_children.size(); ++i) 593 totalSubItems += m_children[i]->showTreeWithIndent(indentLevel + 1); 594 return totalSubItems + 1; 595} 596 597#endif 598 599} // namespace WebCore 600 601#ifndef NDEBUG 602 603int showTree(const WebCore::HistoryItem* item) 604{ 605 return item->showTree(); 606} 607 608#endif 609