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