2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "core/page/HistoryController.h"
34 #include "core/frame/Frame.h"
35 #include "core/loader/FrameLoader.h"
36 #include "core/page/FrameTree.h"
37 #include "core/page/Page.h"
38 #include "wtf/Deque.h"
39 #include "wtf/text/StringHash.h"
43 PassOwnPtr<HistoryNode> HistoryNode::create(HistoryEntry* entry, HistoryItem* value)
45 return adoptPtr(new HistoryNode(entry, value));
48 HistoryNode* HistoryNode::addChild(PassRefPtr<HistoryItem> item)
50 m_children.append(HistoryNode::create(m_entry, item.get()));
51 return m_children.last().get();
54 PassOwnPtr<HistoryNode> HistoryNode::cloneAndReplace(HistoryEntry* newEntry, HistoryItem* newItem, bool clipAtTarget, Frame* targetFrame, Frame* currentFrame)
56 bool isNodeBeingNavigated = targetFrame == currentFrame;
57 HistoryItem* itemForCreate = isNodeBeingNavigated ? newItem : m_value.get();
58 OwnPtr<HistoryNode> newHistoryNode = create(newEntry, itemForCreate);
60 if (!clipAtTarget || !isNodeBeingNavigated) {
61 for (Frame* child = currentFrame->tree().firstChild(); child; child = child->tree().nextSibling()) {
62 HistoryNode* childHistoryNode = m_entry->historyNodeForFrame(child);
63 if (!childHistoryNode)
65 newHistoryNode->m_children.append(childHistoryNode->cloneAndReplace(newEntry, newItem, clipAtTarget, targetFrame, child));
68 return newHistoryNode.release();
71 HistoryNode::HistoryNode(HistoryEntry* entry, HistoryItem* value)
75 m_entry->m_framesToItems.add(value->targetFrameID(), this);
76 String target = value->target();
78 target = emptyString();
79 m_entry->m_uniqueNamesToItems.add(target, this);
82 void HistoryNode::removeChildren()
84 // FIXME: This is inefficient. Figure out a cleaner way to ensure this HistoryNode isn't cached anywhere.
85 for (unsigned i = 0; i < m_children.size(); i++) {
86 m_children[i]->removeChildren();
88 HashMap<uint64_t, HistoryNode*>::iterator framesEnd = m_entry->m_framesToItems.end();
89 HashMap<String, HistoryNode*>::iterator uniqueNamesEnd = m_entry->m_uniqueNamesToItems.end();
90 for (HashMap<uint64_t, HistoryNode*>::iterator it = m_entry->m_framesToItems.begin(); it != framesEnd; ++it) {
91 if (it->value == m_children[i])
92 m_entry->m_framesToItems.remove(it);
94 for (HashMap<String, HistoryNode*>::iterator it = m_entry->m_uniqueNamesToItems.begin(); it != uniqueNamesEnd; ++it) {
95 if (it->value == m_children[i])
96 m_entry->m_uniqueNamesToItems.remove(it);
102 HistoryEntry::HistoryEntry(HistoryItem* root)
104 m_root = HistoryNode::create(this, root);
107 PassOwnPtr<HistoryEntry> HistoryEntry::create(HistoryItem* root)
109 return adoptPtr(new HistoryEntry(root));
112 PassOwnPtr<HistoryEntry> HistoryEntry::cloneAndReplace(HistoryItem* newItem, bool clipAtTarget, Frame* targetFrame, Page* page)
114 OwnPtr<HistoryEntry> newEntry = adoptPtr(new HistoryEntry());
115 newEntry->m_root = m_root->cloneAndReplace(newEntry.get(), newItem, clipAtTarget, targetFrame, page->mainFrame());
116 return newEntry.release();
119 HistoryNode* HistoryEntry::historyNodeForFrame(Frame* frame)
121 if (HistoryNode* historyNode = m_framesToItems.get(frame->frameID()))
123 String target = frame->tree().uniqueName();
125 target = emptyString();
126 return m_uniqueNamesToItems.get(target);
129 HistoryItem* HistoryEntry::itemForFrame(Frame* frame)
131 if (HistoryNode* historyNode = historyNodeForFrame(frame))
132 return historyNode->value();
136 HistoryController::HistoryController(Page* page)
138 , m_defersLoading(false)
139 , m_deferredCachePolicy(UseProtocolCachePolicy)
143 HistoryController::~HistoryController()
147 void HistoryController::updateBackForwardListForFragmentScroll(Frame* frame, HistoryItem* item)
149 createNewBackForwardItem(frame, item, false);
152 void HistoryController::goToEntry(PassOwnPtr<HistoryEntry> targetEntry, ResourceRequestCachePolicy cachePolicy)
154 HistoryFrameLoadSet sameDocumentLoads;
155 HistoryFrameLoadSet differentDocumentLoads;
157 m_provisionalEntry = targetEntry;
159 recursiveGoToEntry(m_page->mainFrame(), sameDocumentLoads, differentDocumentLoads);
161 differentDocumentLoads.set(m_page->mainFrame(), m_provisionalEntry->root());
163 if (sameDocumentLoads.isEmpty() && differentDocumentLoads.isEmpty())
164 sameDocumentLoads.set(m_page->mainFrame(), m_provisionalEntry->root());
166 if (differentDocumentLoads.isEmpty()) {
167 m_previousEntry = m_currentEntry.release();
168 m_currentEntry = m_provisionalEntry.release();
171 for (HistoryFrameLoadSet::iterator it = sameDocumentLoads.begin(); it != sameDocumentLoads.end(); ++it) {
173 it->key->loader().loadHistoryItem(it->value.get(), HistorySameDocumentLoad, cachePolicy);
175 for (HistoryFrameLoadSet::iterator it = differentDocumentLoads.begin(); it != differentDocumentLoads.end(); ++it) {
177 it->key->loader().loadHistoryItem(it->value.get(), HistoryDifferentDocumentLoad, cachePolicy);
181 void HistoryController::recursiveGoToEntry(Frame* frame, HistoryFrameLoadSet& sameDocumentLoads, HistoryFrameLoadSet& differentDocumentLoads)
183 ASSERT(m_provisionalEntry);
184 ASSERT(m_currentEntry);
185 HistoryItem* newItem = m_provisionalEntry->itemForFrame(frame);
186 HistoryItem* oldItem = m_currentEntry->itemForFrame(frame);
190 if (!oldItem || (newItem != oldItem && newItem->itemSequenceNumber() != oldItem->itemSequenceNumber())) {
191 if (oldItem && newItem->documentSequenceNumber() == oldItem->documentSequenceNumber())
192 sameDocumentLoads.set(frame, newItem);
194 differentDocumentLoads.set(frame, newItem);
198 for (Frame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling())
199 recursiveGoToEntry(child, sameDocumentLoads, differentDocumentLoads);
202 void HistoryController::goToItem(HistoryItem* targetItem, ResourceRequestCachePolicy cachePolicy)
204 if (m_defersLoading) {
205 m_deferredItem = targetItem;
206 m_deferredCachePolicy = cachePolicy;
210 OwnPtr<HistoryEntry> newEntry = HistoryEntry::create(targetItem);
211 Deque<HistoryNode*> historyNodes;
212 historyNodes.append(newEntry->rootHistoryNode());
213 while (!historyNodes.isEmpty()) {
214 // For each item, read the children (if any) off the HistoryItem,
215 // create a new HistoryNode for each child and attach it,
216 // then clear the children on the HistoryItem.
217 HistoryNode* historyNode = historyNodes.takeFirst();
218 const HistoryItemVector& children = historyNode->value()->children();
219 for (size_t i = 0; i < children.size(); i++) {
220 HistoryNode* childHistoryNode = historyNode->addChild(children[i].get());
221 historyNodes.append(childHistoryNode);
223 historyNode->value()->clearChildren();
225 goToEntry(newEntry.release(), cachePolicy);
228 void HistoryController::setDefersLoading(bool defer)
230 m_defersLoading = defer;
231 if (!defer && m_deferredItem) {
232 goToItem(m_deferredItem.get(), m_deferredCachePolicy);
234 m_deferredCachePolicy = UseProtocolCachePolicy;
238 void HistoryController::updateForInitialLoadInChildFrame(Frame* frame, HistoryItem* item)
240 ASSERT(frame->tree().parent());
243 if (HistoryNode* existingNode = m_currentEntry->historyNodeForFrame(frame))
244 existingNode->updateValue(item);
245 else if (HistoryNode* parentHistoryNode = m_currentEntry->historyNodeForFrame(frame->tree().parent()))
246 parentHistoryNode->addChild(item);
249 void HistoryController::updateForCommit(Frame* frame, HistoryItem* item, HistoryCommitType commitType)
251 if (commitType == BackForwardCommit) {
252 if (!m_provisionalEntry)
254 // Once committed, we want to use current item for saving DocState, and
255 // the provisional item for restoring state.
256 // Note previousItem must be set before we close the URL, which will
257 // happen when the data source is made non-provisional below
258 m_previousEntry = m_currentEntry.release();
259 ASSERT(m_provisionalEntry);
260 m_currentEntry = m_provisionalEntry.release();
261 } else if (commitType == StandardCommit) {
262 createNewBackForwardItem(frame, item, true);
263 } else if (commitType == InitialCommitInChildFrame) {
264 updateForInitialLoadInChildFrame(frame, item);
268 static PassRefPtr<HistoryItem> itemForExport(HistoryNode* historyNode)
271 RefPtr<HistoryItem> item = historyNode->value()->copy();
272 const Vector<OwnPtr<HistoryNode> >& childEntries = historyNode->children();
273 for (size_t i = 0; i < childEntries.size(); i++)
274 item->addChildItem(itemForExport(childEntries[i].get()));
278 PassRefPtr<HistoryItem> HistoryController::currentItemForExport()
282 return itemForExport(m_currentEntry->rootHistoryNode());
285 PassRefPtr<HistoryItem> HistoryController::previousItemForExport()
287 if (!m_previousEntry)
289 return itemForExport(m_previousEntry->rootHistoryNode());
292 HistoryItem* HistoryController::itemForNewChildFrame(Frame* frame) const
294 return m_currentEntry ? m_currentEntry->itemForFrame(frame) : 0;
297 void HistoryController::removeChildrenForRedirect(Frame* frame)
299 if (!m_provisionalEntry)
301 if (HistoryNode* node = m_provisionalEntry->historyNodeForFrame(frame))
302 node->removeChildren();
305 void HistoryController::createNewBackForwardItem(Frame* targetFrame, HistoryItem* item, bool clipAtTarget)
307 RefPtr<HistoryItem> newItem = item;
308 if (!m_currentEntry) {
309 m_currentEntry = HistoryEntry::create(newItem.get());
311 HistoryItem* oldItem = m_currentEntry->itemForFrame(targetFrame);
312 if (!clipAtTarget && oldItem)
313 newItem->setDocumentSequenceNumber(oldItem->documentSequenceNumber());
314 m_previousEntry = m_currentEntry.release();
315 m_currentEntry = m_previousEntry->cloneAndReplace(newItem.get(), clipAtTarget, targetFrame, m_page);
319 } // namespace WebCore