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