• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/loader/HistoryController.h"
33 
34 #include "core/loader/FrameLoader.h"
35 #include "core/frame/Frame.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 
create(HistoryEntry * entry,HistoryItem * value)43 PassOwnPtr<HistoryNode> HistoryNode::create(HistoryEntry* entry, HistoryItem* value)
44 {
45     return adoptPtr(new HistoryNode(entry, value));
46 }
47 
addChild(PassRefPtr<HistoryItem> item)48 HistoryNode* HistoryNode::addChild(PassRefPtr<HistoryItem> item)
49 {
50     m_children.append(HistoryNode::create(m_entry, item.get()));
51     return m_children.last().get();
52 }
53 
cloneAndReplace(HistoryEntry * newEntry,HistoryItem * newItem,bool clipAtTarget,Frame * targetFrame,Frame * currentFrame)54 PassOwnPtr<HistoryNode> HistoryNode::cloneAndReplace(HistoryEntry* newEntry, HistoryItem* newItem, bool clipAtTarget, Frame* targetFrame, Frame* currentFrame)
55 {
56     bool isNodeBeingNavigated = targetFrame == currentFrame;
57     HistoryItem* itemForCreate = isNodeBeingNavigated ? newItem : m_value.get();
58     OwnPtr<HistoryNode> newHistoryNode = create(newEntry, itemForCreate);
59 
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)
64                 continue;
65             newHistoryNode->m_children.append(childHistoryNode->cloneAndReplace(newEntry, newItem, clipAtTarget, targetFrame, child));
66         }
67     }
68     return newHistoryNode.release();
69 }
70 
HistoryNode(HistoryEntry * entry,HistoryItem * value)71 HistoryNode::HistoryNode(HistoryEntry* entry, HistoryItem* value)
72     : m_entry(entry)
73     , m_value(value)
74 {
75     m_entry->m_framesToItems.add(value->targetFrameID(), this);
76     String target = value->target();
77     if (target.isNull())
78         target = emptyString();
79     m_entry->m_uniqueNamesToItems.add(target, this);
80 }
81 
removeChildren()82 void HistoryNode::removeChildren()
83 {
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();
87 
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);
93         }
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);
97         }
98     }
99     m_children.clear();
100 }
101 
HistoryEntry(HistoryItem * root)102 HistoryEntry::HistoryEntry(HistoryItem* root)
103 {
104     m_root = HistoryNode::create(this, root);
105 }
106 
create(HistoryItem * root)107 PassOwnPtr<HistoryEntry> HistoryEntry::create(HistoryItem* root)
108 {
109     return adoptPtr(new HistoryEntry(root));
110 }
111 
cloneAndReplace(HistoryItem * newItem,bool clipAtTarget,Frame * targetFrame,Page * page)112 PassOwnPtr<HistoryEntry> HistoryEntry::cloneAndReplace(HistoryItem* newItem, bool clipAtTarget, Frame* targetFrame, Page* page)
113 {
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();
117 }
118 
historyNodeForFrame(Frame * frame)119 HistoryNode* HistoryEntry::historyNodeForFrame(Frame* frame)
120 {
121     if (HistoryNode* historyNode = m_framesToItems.get(frame->frameID()))
122         return historyNode;
123     String target = frame->tree().uniqueName();
124     if (target.isNull())
125         target = emptyString();
126     return m_uniqueNamesToItems.get(target);
127 }
128 
itemForFrame(Frame * frame)129 HistoryItem* HistoryEntry::itemForFrame(Frame* frame)
130 {
131     if (HistoryNode* historyNode = historyNodeForFrame(frame))
132         return historyNode->value();
133     return 0;
134 }
135 
HistoryController(Page * page)136 HistoryController::HistoryController(Page* page)
137     : m_page(page)
138     , m_defersLoading(false)
139 {
140 }
141 
~HistoryController()142 HistoryController::~HistoryController()
143 {
144 }
145 
updateBackForwardListForFragmentScroll(Frame * frame,HistoryItem * item)146 void HistoryController::updateBackForwardListForFragmentScroll(Frame* frame, HistoryItem* item)
147 {
148     m_provisionalEntry.clear();
149     createNewBackForwardItem(frame, item, false);
150 }
151 
goToEntry(PassOwnPtr<HistoryEntry> targetEntry)152 void HistoryController::goToEntry(PassOwnPtr<HistoryEntry> targetEntry)
153 {
154     ASSERT(m_sameDocumentLoadsInProgress.isEmpty());
155     ASSERT(m_differentDocumentLoadsInProgress.isEmpty());
156 
157     m_provisionalEntry = targetEntry;
158     if (m_currentEntry)
159         recursiveGoToEntry(m_page->mainFrame());
160     else
161         m_differentDocumentLoadsInProgress.set(m_page->mainFrame(), m_provisionalEntry->root());
162 
163     if (m_sameDocumentLoadsInProgress.isEmpty() && m_differentDocumentLoadsInProgress.isEmpty())
164         m_sameDocumentLoadsInProgress.set(m_page->mainFrame(), m_provisionalEntry->root());
165 
166     if (m_differentDocumentLoadsInProgress.isEmpty()) {
167         m_previousEntry = m_currentEntry.release();
168         m_currentEntry = m_provisionalEntry.release();
169     } else {
170         m_page->mainFrame()->loader().stopAllLoaders();
171     }
172 
173     for (HistoryFrameLoadSet::iterator it = m_sameDocumentLoadsInProgress.begin(); it != m_sameDocumentLoadsInProgress.end(); ++it)
174         it->key->loader().loadHistoryItem(it->value.get(), HistorySameDocumentLoad);
175     for (HistoryFrameLoadSet::iterator it = m_differentDocumentLoadsInProgress.begin(); it != m_differentDocumentLoadsInProgress.end(); ++it)
176         it->key->loader().loadHistoryItem(it->value.get(), HistoryDifferentDocumentLoad);
177     m_sameDocumentLoadsInProgress.clear();
178     m_differentDocumentLoadsInProgress.clear();
179 }
180 
recursiveGoToEntry(Frame * frame)181 void HistoryController::recursiveGoToEntry(Frame* frame)
182 {
183     ASSERT(m_provisionalEntry);
184     ASSERT(m_currentEntry);
185     HistoryItem* newItem = m_provisionalEntry->itemForFrame(frame);
186     HistoryItem* oldItem = m_currentEntry->itemForFrame(frame);
187     if (!newItem)
188         return;
189 
190     if (!oldItem || (newItem != oldItem && newItem->itemSequenceNumber() != oldItem->itemSequenceNumber())) {
191         if (oldItem && newItem->documentSequenceNumber() == oldItem->documentSequenceNumber())
192             m_sameDocumentLoadsInProgress.set(frame, newItem);
193         else
194             m_differentDocumentLoadsInProgress.set(frame, newItem);
195         return;
196     }
197 
198     for (Frame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling())
199         recursiveGoToEntry(child);
200 }
201 
goToItem(HistoryItem * targetItem)202 void HistoryController::goToItem(HistoryItem* targetItem)
203 {
204     if (m_defersLoading) {
205         m_deferredItem = targetItem;
206         return;
207     }
208 
209     OwnPtr<HistoryEntry> newEntry = HistoryEntry::create(targetItem);
210     Deque<HistoryNode*> historyNodes;
211     historyNodes.append(newEntry->rootHistoryNode());
212     while (!historyNodes.isEmpty()) {
213         // For each item, read the children (if any) off the HistoryItem,
214         // create a new HistoryNode for each child and attach it,
215         // then clear the children on the HistoryItem.
216         HistoryNode* historyNode = historyNodes.takeFirst();
217         const HistoryItemVector& children = historyNode->value()->children();
218         for (size_t i = 0; i < children.size(); i++) {
219             HistoryNode* childHistoryNode = historyNode->addChild(children[i].get());
220             historyNodes.append(childHistoryNode);
221         }
222         historyNode->value()->clearChildren();
223     }
224     goToEntry(newEntry.release());
225 }
226 
setDefersLoading(bool defer)227 void HistoryController::setDefersLoading(bool defer)
228 {
229     m_defersLoading = defer;
230     if (!defer && m_deferredItem) {
231         goToItem(m_deferredItem.get());
232         m_deferredItem = 0;
233     }
234 }
235 
updateForInitialLoadInChildFrame(Frame * frame,HistoryItem * item)236 void HistoryController::updateForInitialLoadInChildFrame(Frame* frame, HistoryItem* item)
237 {
238     ASSERT(frame->tree().parent());
239     if (!m_currentEntry)
240         return;
241     if (HistoryNode* existingChildHistoryNode = m_currentEntry->historyNodeForFrame(frame))
242         existingChildHistoryNode->updateValue(item);
243     else if (HistoryNode* parentHistoryNode = m_currentEntry->historyNodeForFrame(frame->tree().parent()))
244         parentHistoryNode->addChild(item);
245 }
246 
247 // FIXME: This is a temporary hack designed to be mergeable to the 1750 branch.
248 // As trunk stands currently, we should never clear the provisional entry, since it's
249 // possible to clear based on a commit in an irrelevant frame. On trunk, the provisional entry is
250 // an implementation detail of HistoryController and only used when we know that we're in
251 // a back/forward navigation. Also, it is clobbered when a new history navigation begins,
252 // so we can be sure that a stale provisional entry won't be confused with a new one.
253 // On the branch, however, the provisional entry is observable because
254 // WebFrameImpl::currentHistoryItem() will return data based on the provisional entry preferentially
255 // over the current entry, so we can't leave a stale provisional entry around indefinitely.
256 // Therefore, search the frame tree for any back/forward navigations in progress, and only clear
257 // the provisional entry if none are found.
258 // Once the fix is merged to the branch, this can be removed, along with all places that we clear
259 // m_provisionalEntry.
shouldClearProvisionalEntry(Page * page)260 static bool shouldClearProvisionalEntry(Page* page)
261 {
262     for (Frame* frame = page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
263         if (frame->loader().loadType() == FrameLoadTypeBackForward)
264             return false;
265     }
266     return true;
267 }
268 
updateForCommit(Frame * frame,HistoryItem * item)269 void HistoryController::updateForCommit(Frame* frame, HistoryItem* item)
270 {
271     FrameLoadType type = frame->loader().loadType();
272     if (isBackForwardLoadType(type) && m_provisionalEntry) {
273         // Once committed, we want to use current item for saving DocState, and
274         // the provisional item for restoring state.
275         // Note previousItem must be set before we close the URL, which will
276         // happen when the data source is made non-provisional below
277         m_previousEntry = m_currentEntry.release();
278         ASSERT(m_provisionalEntry);
279         m_currentEntry = m_provisionalEntry.release();
280     } else if (type != FrameLoadTypeRedirectWithLockedBackForwardList && shouldClearProvisionalEntry(m_page)) {
281         m_provisionalEntry.clear();
282     }
283 
284     if (type == FrameLoadTypeStandard)
285         createNewBackForwardItem(frame, item, true);
286     else if (type == FrameLoadTypeInitialInChildFrame)
287         updateForInitialLoadInChildFrame(frame, item);
288 }
289 
itemForExport(HistoryNode * historyNode)290 static PassRefPtr<HistoryItem> itemForExport(HistoryNode* historyNode)
291 {
292     RefPtr<HistoryItem> item = historyNode->value()->copy();
293     const Vector<OwnPtr<HistoryNode> >& childEntries = historyNode->children();
294     for (size_t i = 0; i < childEntries.size(); i++)
295         item->addChildItem(itemForExport(childEntries[i].get()));
296     return item;
297 }
298 
currentItemForExport()299 PassRefPtr<HistoryItem> HistoryController::currentItemForExport()
300 {
301     if (!m_currentEntry)
302         return 0;
303     return itemForExport(m_currentEntry->rootHistoryNode());
304 }
305 
previousItemForExport()306 PassRefPtr<HistoryItem> HistoryController::previousItemForExport()
307 {
308     if (!m_previousEntry)
309         return 0;
310     return itemForExport(m_previousEntry->rootHistoryNode());
311 }
312 
provisionalItemForExport()313 PassRefPtr<HistoryItem> HistoryController::provisionalItemForExport()
314 {
315     if (!m_provisionalEntry)
316         return 0;
317     return itemForExport(m_provisionalEntry->rootHistoryNode());
318 }
319 
itemForNewChildFrame(Frame * frame) const320 HistoryItem* HistoryController::itemForNewChildFrame(Frame* frame) const
321 {
322     return m_currentEntry ? m_currentEntry->itemForFrame(frame) : 0;
323 }
324 
removeChildrenForRedirect(Frame * frame)325 void HistoryController::removeChildrenForRedirect(Frame* frame)
326 {
327     if (!m_provisionalEntry)
328         return;
329     if (HistoryNode* node = m_provisionalEntry->historyNodeForFrame(frame))
330         node->removeChildren();
331 }
332 
createNewBackForwardItem(Frame * targetFrame,HistoryItem * item,bool clipAtTarget)333 void HistoryController::createNewBackForwardItem(Frame* targetFrame, HistoryItem* item, bool clipAtTarget)
334 {
335     RefPtr<HistoryItem> newItem = item;
336     if (!m_currentEntry) {
337         m_currentEntry = HistoryEntry::create(newItem.get());
338     } else {
339         HistoryItem* oldItem = m_currentEntry->itemForFrame(targetFrame);
340         if (!clipAtTarget && oldItem)
341             newItem->setDocumentSequenceNumber(oldItem->documentSequenceNumber());
342         m_previousEntry = m_currentEntry.release();
343         m_currentEntry = m_previousEntry->cloneAndReplace(newItem.get(), clipAtTarget, targetFrame, m_page);
344     }
345 }
346 
347 } // namespace WebCore
348