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