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