1/*
2 * Copyright (C) 2010-2011, 2016 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. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebBackForwardListItem.h"
28
29#include "SuspendedPageProxy.h"
30#include "WebProcessPool.h"
31#include "WebProcessProxy.h"
32#include <wtf/DebugUtilities.h>
33#include <wtf/URL.h>
34
35namespace WebKit {
36using namespace WebCore;
37
38Ref<WebBackForwardListItem> WebBackForwardListItem::create(BackForwardListItemState&& backForwardListItemState, PageIdentifier pageID)
39{
40 return adoptRef(*new WebBackForwardListItem(WTFMove(backForwardListItemState), pageID));
41}
42
43WebBackForwardListItem::WebBackForwardListItem(BackForwardListItemState&& backForwardListItemState, PageIdentifier pageID)
44 : m_itemState(WTFMove(backForwardListItemState))
45 , m_pageID(pageID)
46 , m_lastProcessIdentifier(m_itemState.identifier.processIdentifier)
47{
48 auto result = allItems().add(m_itemState.identifier, this);
49 ASSERT_UNUSED(result, result.isNewEntry);
50}
51
52WebBackForwardListItem::~WebBackForwardListItem()
53{
54 ASSERT(allItems().get(m_itemState.identifier) == this);
55 allItems().remove(m_itemState.identifier);
56
57 removeSuspendedPageFromProcessPool();
58}
59
60HashMap<BackForwardItemIdentifier, WebBackForwardListItem*>& WebBackForwardListItem::allItems()
61{
62 static NeverDestroyed<HashMap<BackForwardItemIdentifier, WebBackForwardListItem*>> items;
63 return items;
64}
65
66WebBackForwardListItem* WebBackForwardListItem::itemForID(const BackForwardItemIdentifier& identifier)
67{
68 return allItems().get(identifier);
69}
70
71static const FrameState* childItemWithDocumentSequenceNumber(const FrameState& frameState, int64_t number)
72{
73 for (const auto& child : frameState.children) {
74 if (child.documentSequenceNumber == number)
75 return &child;
76 }
77
78 return nullptr;
79}
80
81static const FrameState* childItemWithTarget(const FrameState& frameState, const String& target)
82{
83 for (const auto& child : frameState.children) {
84 if (child.target == target)
85 return &child;
86 }
87
88 return nullptr;
89}
90
91static bool documentTreesAreEqual(const FrameState& a, const FrameState& b)
92{
93 if (a.documentSequenceNumber != b.documentSequenceNumber)
94 return false;
95
96 if (a.children.size() != b.children.size())
97 return false;
98
99 for (const auto& child : a.children) {
100 const FrameState* otherChild = childItemWithDocumentSequenceNumber(b, child.documentSequenceNumber);
101 if (!otherChild || !documentTreesAreEqual(child, *otherChild))
102 return false;
103 }
104
105 return true;
106}
107
108bool WebBackForwardListItem::itemIsInSameDocument(const WebBackForwardListItem& other) const
109{
110 if (m_pageID != other.m_pageID)
111 return false;
112
113 // The following logic must be kept in sync with WebCore::HistoryItem::shouldDoSameDocumentNavigationTo().
114
115 const FrameState& mainFrameState = m_itemState.pageState.mainFrameState;
116 const FrameState& otherMainFrameState = other.m_itemState.pageState.mainFrameState;
117
118 if (mainFrameState.stateObjectData || otherMainFrameState.stateObjectData)
119 return mainFrameState.documentSequenceNumber == otherMainFrameState.documentSequenceNumber;
120
121 URL url = URL({ }, mainFrameState.urlString);
122 URL otherURL = URL({ }, otherMainFrameState.urlString);
123
124 if ((url.hasFragmentIdentifier() || otherURL.hasFragmentIdentifier()) && equalIgnoringFragmentIdentifier(url, otherURL))
125 return mainFrameState.documentSequenceNumber == otherMainFrameState.documentSequenceNumber;
126
127 return documentTreesAreEqual(mainFrameState, otherMainFrameState);
128}
129
130static bool hasSameFrames(const FrameState& a, const FrameState& b)
131{
132 if (a.target != b.target)
133 return false;
134
135 if (a.children.size() != b.children.size())
136 return false;
137
138 for (const auto& child : a.children) {
139 if (!childItemWithTarget(b, child.target))
140 return false;
141 }
142
143 return true;
144}
145
146bool WebBackForwardListItem::itemIsClone(const WebBackForwardListItem& other)
147{
148 // The following logic must be kept in sync with WebCore::HistoryItem::itemsAreClones().
149
150 if (this == &other)
151 return false;
152
153 const FrameState& mainFrameState = m_itemState.pageState.mainFrameState;
154 const FrameState& otherMainFrameState = other.m_itemState.pageState.mainFrameState;
155
156 if (mainFrameState.itemSequenceNumber != otherMainFrameState.itemSequenceNumber)
157 return false;
158
159 return hasSameFrames(mainFrameState, otherMainFrameState);
160}
161
162void WebBackForwardListItem::setSuspendedPage(SuspendedPageProxy* page)
163{
164 if (m_suspendedPage == page)
165 return;
166
167 removeSuspendedPageFromProcessPool();
168 m_suspendedPage = makeWeakPtr(page);
169}
170
171SuspendedPageProxy* WebBackForwardListItem::suspendedPage() const
172{
173 return m_suspendedPage.get();
174}
175
176void WebBackForwardListItem::removeSuspendedPageFromProcessPool()
177{
178 if (!m_suspendedPage)
179 return;
180
181 m_suspendedPage->process().processPool().removeSuspendedPage(*m_suspendedPage);
182 ASSERT(!m_suspendedPage);
183}
184
185#if !LOG_DISABLED
186const char* WebBackForwardListItem::loggingString()
187{
188 return debugString("Back/forward item ID ", itemID().logString(), ", original URL ", originalURL(), ", current URL ", url(), m_suspendedPage ? "(has a suspended page)" : "");
189}
190#endif // !LOG_DISABLED
191
192} // namespace WebKit
193