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