Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / page / HistoryController.cpp
1 /*
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/)
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
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.
18  *
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.
29  */
30
31 #include "config.h"
32 #include "core/page/HistoryController.h"
33
34 #include "core/frame/LocalFrame.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"
40
41 namespace WebCore {
42
43 PassOwnPtr<HistoryNode> HistoryNode::create(HistoryEntry* entry, HistoryItem* value, int64_t frameID)
44 {
45     return adoptPtr(new HistoryNode(entry, value, frameID));
46 }
47
48 HistoryNode* HistoryNode::addChild(PassRefPtr<HistoryItem> item, int64_t frameID)
49 {
50     m_children.append(HistoryNode::create(m_entry, item.get(), frameID));
51     return m_children.last().get();
52 }
53
54 PassOwnPtr<HistoryNode> HistoryNode::cloneAndReplace(HistoryEntry* newEntry, HistoryItem* newItem, bool clipAtTarget, LocalFrame* targetFrame, LocalFrame* currentFrame)
55 {
56     bool isNodeBeingNavigated = targetFrame == currentFrame;
57     HistoryItem* itemForCreate = isNodeBeingNavigated ? newItem : m_value.get();
58     OwnPtr<HistoryNode> newHistoryNode = create(newEntry, itemForCreate, currentFrame->frameID());
59
60     if (!clipAtTarget || !isNodeBeingNavigated) {
61         for (LocalFrame* child = currentFrame->tree().firstChild(); child; child = child->tree().nextSibling()) {
62             HistoryNode* childHistoryNode = m_entry->historyNodeForFrame(child);
63             if (!childHistoryNode)
64                 continue;
65             newHistoryNode->m_children.append(childHistoryNode->cloneAndReplace(newEntry, newItem, clipAtTarget, targetFrame, child));
66         }
67     }
68     return newHistoryNode.release();
69 }
70
71 HistoryNode::HistoryNode(HistoryEntry* entry, HistoryItem* value, int64_t frameID)
72     : m_entry(entry)
73     , m_value(value)
74 {
75     if (frameID != -1)
76         m_entry->m_framesToItems.add(frameID, this);
77     String target = value->target();
78     if (target.isNull())
79         target = emptyString();
80     m_entry->m_uniqueNamesToItems.add(target, this);
81 }
82
83 void HistoryNode::removeChildren()
84 {
85     // FIXME: This is inefficient. Figure out a cleaner way to ensure this HistoryNode isn't cached anywhere.
86     for (unsigned i = 0; i < m_children.size(); i++) {
87         m_children[i]->removeChildren();
88
89         HashMap<uint64_t, HistoryNode*>::iterator framesEnd = m_entry->m_framesToItems.end();
90         HashMap<String, HistoryNode*>::iterator uniqueNamesEnd = m_entry->m_uniqueNamesToItems.end();
91         for (HashMap<uint64_t, HistoryNode*>::iterator it = m_entry->m_framesToItems.begin(); it != framesEnd; ++it) {
92             if (it->value == m_children[i])
93                 m_entry->m_framesToItems.remove(it);
94         }
95         for (HashMap<String, HistoryNode*>::iterator it = m_entry->m_uniqueNamesToItems.begin(); it != uniqueNamesEnd; ++it) {
96             if (it->value == m_children[i])
97                 m_entry->m_uniqueNamesToItems.remove(it);
98         }
99     }
100     m_children.clear();
101 }
102
103 HistoryEntry::HistoryEntry(HistoryItem* root, int64_t frameID)
104 {
105     m_root = HistoryNode::create(this, root, frameID);
106 }
107
108 PassOwnPtr<HistoryEntry> HistoryEntry::create(HistoryItem* root, int64_t frameID)
109 {
110     return adoptPtr(new HistoryEntry(root, frameID));
111 }
112
113 PassOwnPtr<HistoryEntry> HistoryEntry::cloneAndReplace(HistoryItem* newItem, bool clipAtTarget, LocalFrame* targetFrame, Page* page)
114 {
115     OwnPtr<HistoryEntry> newEntry = adoptPtr(new HistoryEntry());
116     newEntry->m_root = m_root->cloneAndReplace(newEntry.get(), newItem, clipAtTarget, targetFrame, page->mainFrame());
117     return newEntry.release();
118 }
119
120 HistoryNode* HistoryEntry::historyNodeForFrame(LocalFrame* frame)
121 {
122     if (HistoryNode* historyNode = m_framesToItems.get(frame->frameID()))
123         return historyNode;
124     String target = frame->tree().uniqueName();
125     if (target.isNull())
126         target = emptyString();
127     return m_uniqueNamesToItems.get(target);
128 }
129
130 HistoryItem* HistoryEntry::itemForFrame(LocalFrame* frame)
131 {
132     if (HistoryNode* historyNode = historyNodeForFrame(frame))
133         return historyNode->value();
134     return 0;
135 }
136
137 HistoryController::HistoryController(Page* page)
138     : m_page(page)
139 {
140 }
141
142 HistoryController::~HistoryController()
143 {
144 }
145
146 void HistoryController::updateBackForwardListForFragmentScroll(LocalFrame* frame, HistoryItem* item)
147 {
148     createNewBackForwardItem(frame, item, false);
149 }
150
151 void HistoryController::goToEntry(PassOwnPtr<HistoryEntry> targetEntry, ResourceRequestCachePolicy cachePolicy)
152 {
153     HistoryFrameLoadSet sameDocumentLoads;
154     HistoryFrameLoadSet differentDocumentLoads;
155
156     m_provisionalEntry = targetEntry;
157     if (m_currentEntry)
158         recursiveGoToEntry(m_page->mainFrame(), sameDocumentLoads, differentDocumentLoads);
159     else
160         differentDocumentLoads.set(m_page->mainFrame(), m_provisionalEntry->root());
161
162     if (sameDocumentLoads.isEmpty() && differentDocumentLoads.isEmpty())
163         sameDocumentLoads.set(m_page->mainFrame(), m_provisionalEntry->root());
164
165     if (differentDocumentLoads.isEmpty()) {
166         m_previousEntry = m_currentEntry.release();
167         m_currentEntry = m_provisionalEntry.release();
168     }
169
170     for (HistoryFrameLoadSet::iterator it = sameDocumentLoads.begin(); it != sameDocumentLoads.end(); ++it) {
171         if (it->key->host())
172             it->key->loader().loadHistoryItem(it->value.get(), HistorySameDocumentLoad, cachePolicy);
173     }
174     for (HistoryFrameLoadSet::iterator it = differentDocumentLoads.begin(); it != differentDocumentLoads.end(); ++it) {
175         if (it->key->host())
176             it->key->loader().loadHistoryItem(it->value.get(), HistoryDifferentDocumentLoad, cachePolicy);
177     }
178 }
179
180 void HistoryController::recursiveGoToEntry(LocalFrame* frame, HistoryFrameLoadSet& sameDocumentLoads, HistoryFrameLoadSet& differentDocumentLoads)
181 {
182     ASSERT(m_provisionalEntry);
183     ASSERT(m_currentEntry);
184     HistoryItem* newItem = m_provisionalEntry->itemForFrame(frame);
185     HistoryItem* oldItem = m_currentEntry->itemForFrame(frame);
186     if (!newItem)
187         return;
188
189     if (!oldItem || (newItem != oldItem && newItem->itemSequenceNumber() != oldItem->itemSequenceNumber())) {
190         if (oldItem && newItem->documentSequenceNumber() == oldItem->documentSequenceNumber())
191             sameDocumentLoads.set(frame, newItem);
192         else
193             differentDocumentLoads.set(frame, newItem);
194         return;
195     }
196
197     for (LocalFrame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling())
198         recursiveGoToEntry(child, sameDocumentLoads, differentDocumentLoads);
199 }
200
201 void HistoryController::goToItem(HistoryItem* targetItem, ResourceRequestCachePolicy cachePolicy)
202 {
203     // We don't have enough information to set a correct frame id here. This might be a restore from
204     // disk, and the frame ids might not match up if the state was saved from a different process.
205     // Ensure the HistoryEntry's main frame id matches the actual main frame id. Its subframe ids
206     // are invalid to ensure they don't accidentally match a potentially random frame.
207     OwnPtr<HistoryEntry> newEntry = HistoryEntry::create(targetItem, m_page->mainFrame()->frameID());
208     Deque<HistoryNode*> historyNodes;
209     historyNodes.append(newEntry->rootHistoryNode());
210     while (!historyNodes.isEmpty()) {
211         // For each item, read the children (if any) off the HistoryItem,
212         // create a new HistoryNode for each child and attach it,
213         // then clear the children on the HistoryItem.
214         HistoryNode* historyNode = historyNodes.takeFirst();
215         const HistoryItemVector& children = historyNode->value()->children();
216         for (size_t i = 0; i < children.size(); i++) {
217             HistoryNode* childHistoryNode = historyNode->addChild(children[i].get(), -1);
218             historyNodes.append(childHistoryNode);
219         }
220         historyNode->value()->clearChildren();
221     }
222     goToEntry(newEntry.release(), cachePolicy);
223 }
224
225 void HistoryController::updateForInitialLoadInChildFrame(LocalFrame* frame, HistoryItem* item)
226 {
227     ASSERT(frame->tree().parent());
228     if (!m_currentEntry)
229         return;
230     if (HistoryNode* existingNode = m_currentEntry->historyNodeForFrame(frame))
231         existingNode->updateValue(item);
232     else if (HistoryNode* parentHistoryNode = m_currentEntry->historyNodeForFrame(frame->tree().parent()))
233         parentHistoryNode->addChild(item, frame->frameID());
234 }
235
236 void HistoryController::updateForCommit(LocalFrame* frame, HistoryItem* item, HistoryCommitType commitType)
237 {
238     if (commitType == BackForwardCommit) {
239         if (!m_provisionalEntry)
240             return;
241         // Once committed, we want to use current item for saving DocState, and
242         // the provisional item for restoring state.
243         // Note previousItem must be set before we close the URL, which will
244         // happen when the data source is made non-provisional below
245         m_previousEntry = m_currentEntry.release();
246         ASSERT(m_provisionalEntry);
247         m_currentEntry = m_provisionalEntry.release();
248     } else if (commitType == StandardCommit) {
249         createNewBackForwardItem(frame, item, true);
250     } else if (commitType == InitialCommitInChildFrame) {
251         updateForInitialLoadInChildFrame(frame, item);
252     }
253 }
254
255 static PassRefPtr<HistoryItem> itemForExport(HistoryNode* historyNode)
256 {
257     ASSERT(historyNode);
258     RefPtr<HistoryItem> item = historyNode->value()->copy();
259     const Vector<OwnPtr<HistoryNode> >& childEntries = historyNode->children();
260     for (size_t i = 0; i < childEntries.size(); i++)
261         item->addChildItem(itemForExport(childEntries[i].get()));
262     return item;
263 }
264
265 PassRefPtr<HistoryItem> HistoryController::currentItemForExport()
266 {
267     if (!m_currentEntry)
268         return nullptr;
269     return itemForExport(m_currentEntry->rootHistoryNode());
270 }
271
272 PassRefPtr<HistoryItem> HistoryController::previousItemForExport()
273 {
274     if (!m_previousEntry)
275         return nullptr;
276     return itemForExport(m_previousEntry->rootHistoryNode());
277 }
278
279 HistoryItem* HistoryController::itemForNewChildFrame(LocalFrame* frame) const
280 {
281     return m_currentEntry ? m_currentEntry->itemForFrame(frame) : 0;
282 }
283
284 void HistoryController::removeChildrenForRedirect(LocalFrame* frame)
285 {
286     if (!m_provisionalEntry)
287         return;
288     if (HistoryNode* node = m_provisionalEntry->historyNodeForFrame(frame))
289         node->removeChildren();
290 }
291
292 void HistoryController::createNewBackForwardItem(LocalFrame* targetFrame, HistoryItem* item, bool clipAtTarget)
293 {
294     RefPtr<HistoryItem> newItem = item;
295     if (!m_currentEntry) {
296         m_currentEntry = HistoryEntry::create(newItem.get(), targetFrame->frameID());
297     } else {
298         HistoryItem* oldItem = m_currentEntry->itemForFrame(targetFrame);
299         if (!clipAtTarget && oldItem)
300             newItem->setDocumentSequenceNumber(oldItem->documentSequenceNumber());
301         m_previousEntry = m_currentEntry.release();
302         m_currentEntry = m_previousEntry->cloneAndReplace(newItem.get(), clipAtTarget, targetFrame, m_page);
303     }
304 }
305
306 } // namespace WebCore