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