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