OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. | 2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) | 3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) |
4 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.t
orchmobile.com/) | 4 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.t
orchmobile.com/) |
5 * | 5 * |
6 * Redistribution and use in source and binary forms, with or without | 6 * Redistribution and use in source and binary forms, with or without |
7 * modification, are permitted provided that the following conditions | 7 * modification, are permitted provided that the following conditions |
8 * are met: | 8 * are met: |
9 * | 9 * |
10 * 1. Redistributions of source code must retain the above copyright | 10 * 1. Redistributions of source code must retain the above copyright |
(...skipping 27 matching lines...) Expand all Loading... |
38 #include "core/loader/DocumentLoader.h" | 38 #include "core/loader/DocumentLoader.h" |
39 #include "core/loader/FrameLoader.h" | 39 #include "core/loader/FrameLoader.h" |
40 #include "core/loader/FrameLoaderClient.h" | 40 #include "core/loader/FrameLoaderClient.h" |
41 #include "core/loader/FrameLoaderStateMachine.h" | 41 #include "core/loader/FrameLoaderStateMachine.h" |
42 #include "core/frame/Frame.h" | 42 #include "core/frame/Frame.h" |
43 #include "core/frame/FrameView.h" | 43 #include "core/frame/FrameView.h" |
44 #include "core/page/FrameTree.h" | 44 #include "core/page/FrameTree.h" |
45 #include "core/page/Page.h" | 45 #include "core/page/Page.h" |
46 #include "core/page/scrolling/ScrollingCoordinator.h" | 46 #include "core/page/scrolling/ScrollingCoordinator.h" |
47 #include "platform/Logging.h" | 47 #include "platform/Logging.h" |
| 48 #include "wtf/Deque.h" |
48 #include "wtf/text/CString.h" | 49 #include "wtf/text/CString.h" |
49 | 50 |
50 namespace WebCore { | 51 namespace WebCore { |
51 | 52 |
52 HistoryController::HistoryController(Frame* frame) | 53 PassOwnPtr<HistoryNode> HistoryNode::create(HistoryEntry* entry, HistoryItem* va
lue) |
53 : m_frame(frame) | 54 { |
| 55 return adoptPtr(new HistoryNode(entry, value)); |
| 56 } |
| 57 |
| 58 HistoryNode* HistoryNode::addChild(PassRefPtr<HistoryItem> item) |
| 59 { |
| 60 m_children.append(HistoryNode::create(m_entry, item.get())); |
| 61 return m_children.last().get(); |
| 62 } |
| 63 |
| 64 PassOwnPtr<HistoryNode> HistoryNode::cloneAndReplace(HistoryEntry* newEntry, His
toryItem* newItem, HistoryItem* oldItem, bool clipAtTarget, Frame* frame) |
| 65 { |
| 66 bool isNodeBeingNavigated = m_value == oldItem; |
| 67 HistoryItem* itemForCreate = isNodeBeingNavigated ? newItem : m_value.get(); |
| 68 OwnPtr<HistoryNode> newHistoryNode = create(newEntry, itemForCreate); |
| 69 |
| 70 if (!clipAtTarget || !isNodeBeingNavigated) { |
| 71 for (Frame* child = frame->tree().firstChild(); child; child = child->tr
ee().nextSibling()) { |
| 72 HistoryNode* childHistoryNode = m_entry->m_framesToItems.get(child->
frameID()); |
| 73 if (!childHistoryNode) |
| 74 continue; |
| 75 newHistoryNode->m_children.append(childHistoryNode->cloneAndReplace(
newEntry, newItem, oldItem, clipAtTarget, child)); |
| 76 } |
| 77 } |
| 78 return newHistoryNode.release(); |
| 79 } |
| 80 |
| 81 HistoryNode::HistoryNode(HistoryEntry* entry, HistoryItem* value) |
| 82 : m_entry(entry) |
| 83 , m_value(value) |
| 84 { |
| 85 m_entry->m_framesToItems.add(value->targetFrameID(), this); |
| 86 String target = value->target(); |
| 87 if (target.isNull()) |
| 88 target = emptyString(); |
| 89 m_entry->m_uniqueNamesToItems.add(target, this); |
| 90 } |
| 91 |
| 92 HistoryEntry::HistoryEntry(HistoryItem* root) |
| 93 { |
| 94 m_root = HistoryNode::create(this, root); |
| 95 } |
| 96 |
| 97 PassOwnPtr<HistoryEntry> HistoryEntry::create(HistoryItem* root) |
| 98 { |
| 99 return adoptPtr(new HistoryEntry(root)); |
| 100 } |
| 101 |
| 102 PassOwnPtr<HistoryEntry> HistoryEntry::cloneAndReplace(HistoryItem* newItem, His
toryItem* oldItem, bool clipAtTarget, Page* page) |
| 103 { |
| 104 OwnPtr<HistoryEntry> newEntry = adoptPtr(new HistoryEntry()); |
| 105 newEntry->m_root = m_root->cloneAndReplace(newEntry.get(), newItem, oldItem,
clipAtTarget, page->mainFrame()); |
| 106 return newEntry.release(); |
| 107 } |
| 108 |
| 109 HistoryNode* HistoryEntry::historyNodeForFrame(Frame* frame) |
| 110 { |
| 111 if (HistoryNode* historyNode = m_framesToItems.get(frame->frameID())) |
| 112 return historyNode; |
| 113 String target = frame->tree().uniqueName(); |
| 114 if (target.isNull()) |
| 115 target = emptyString(); |
| 116 return m_uniqueNamesToItems.get(target); |
| 117 } |
| 118 |
| 119 HistoryItem* HistoryEntry::itemForFrame(Frame* frame) |
| 120 { |
| 121 if (HistoryNode* historyNode = historyNodeForFrame(frame)) |
| 122 return historyNode->value(); |
| 123 return 0; |
| 124 } |
| 125 |
| 126 HistoryController::HistoryController(Page* page) |
| 127 : m_page(page) |
54 , m_defersLoading(false) | 128 , m_defersLoading(false) |
55 { | 129 { |
56 } | 130 } |
57 | 131 |
58 HistoryController::~HistoryController() | 132 HistoryController::~HistoryController() |
59 { | 133 { |
60 } | 134 } |
61 | 135 |
62 void HistoryController::clearScrollPositionAndViewState() | 136 void HistoryController::clearScrollPositionAndViewState() |
63 { | 137 { |
64 if (!m_currentItem) | 138 if (!m_currentEntry->root()) |
65 return; | 139 return; |
66 | 140 |
67 m_currentItem->clearScrollPoint(); | 141 m_currentEntry->root()->clearScrollPoint(); |
68 m_currentItem->setPageScaleFactor(0); | 142 m_currentEntry->root()->setPageScaleFactor(0); |
69 } | 143 } |
70 | 144 |
71 /* | 145 /* |
72 There is a race condition between the layout and load completion that affects r
estoring the scroll position. | 146 There is a race condition between the layout and load completion that affects r
estoring the scroll position. |
73 We try to restore the scroll position at both the first layout and upon load co
mpletion. | 147 We try to restore the scroll position at both the first layout and upon load co
mpletion. |
74 | 148 |
75 1) If first layout happens before the load completes, we want to restore the sc
roll position then so that the | 149 1) If first layout happens before the load completes, we want to restore the sc
roll position then so that the |
76 first time we draw the page is already scrolled to the right place, instead of
starting at the top and later | 150 first time we draw the page is already scrolled to the right place, instead of
starting at the top and later |
77 jumping down. It is possible that the old scroll position is past the part of
the doc laid out so far, in | 151 jumping down. It is possible that the old scroll position is past the part of
the doc laid out so far, in |
78 which case the restore silent fails and we will fix it in when we try to restor
e on doc completion. | 152 which case the restore silent fails and we will fix it in when we try to restor
e on doc completion. |
79 2) If the layout happens after the load completes, the attempt to restore at lo
ad completion time silently | 153 2) If the layout happens after the load completes, the attempt to restore at lo
ad completion time silently |
80 fails. We then successfully restore it when the layout happens. | 154 fails. We then successfully restore it when the layout happens. |
81 */ | 155 */ |
82 void HistoryController::restoreScrollPositionAndViewState() | 156 void HistoryController::restoreScrollPositionAndViewState(Frame* frame) |
83 { | 157 { |
84 if (!m_currentItem || !m_frame->loader().stateMachine()->committedFirstRealD
ocumentLoad()) | 158 if (!m_currentEntry || !frame->loader().stateMachine()->committedFirstRealDo
cumentLoad()) |
85 return; | 159 return; |
86 | 160 |
87 if (FrameView* view = m_frame->view()) { | 161 if (FrameView* view = frame->view()) { |
88 if (m_frame->isMainFrame()) { | 162 if (frame->isMainFrame()) { |
89 if (ScrollingCoordinator* scrollingCoordinator = m_frame->page()->sc
rollingCoordinator()) | 163 if (ScrollingCoordinator* scrollingCoordinator = m_page->scrollingCo
ordinator()) |
90 scrollingCoordinator->frameViewRootLayerDidChange(view); | 164 scrollingCoordinator->frameViewRootLayerDidChange(view); |
91 } | 165 } |
92 | 166 |
93 if (!view->wasScrolledByUser()) { | 167 if (!view->wasScrolledByUser()) { |
94 if (m_frame->isMainFrame() && m_currentItem->pageScaleFactor()) | 168 if (frame->isMainFrame() && m_currentEntry->root()->pageScaleFactor(
)) |
95 m_frame->page()->setPageScaleFactor(m_currentItem->pageScaleFact
or(), m_currentItem->scrollPoint()); | 169 m_page->setPageScaleFactor(m_currentEntry->root()->pageScaleFact
or(), m_currentEntry->root()->scrollPoint()); |
96 else | 170 else |
97 view->setScrollPositionNonProgrammatically(m_currentItem->scroll
Point()); | 171 view->setScrollPositionNonProgrammatically(m_currentEntry->itemF
orFrame(frame)->scrollPoint()); |
98 } | 172 } |
99 } | 173 } |
100 } | 174 } |
101 | 175 |
102 void HistoryController::updateBackForwardListForFragmentScroll() | 176 void HistoryController::updateBackForwardListForFragmentScroll(Frame* frame) |
103 { | 177 { |
104 createNewBackForwardItem(false); | 178 createNewBackForwardItem(frame, false); |
105 } | 179 } |
106 | 180 |
107 void HistoryController::saveDocumentAndScrollState() | 181 void HistoryController::saveDocumentAndScrollState(Frame* frame) |
108 { | 182 { |
109 if (!m_currentItem) | 183 if (!m_currentEntry || !m_currentEntry->itemForFrame(frame)) |
110 return; | 184 return; |
111 | 185 |
112 Document* document = m_frame->document(); | 186 Document* document = frame->document(); |
113 ASSERT(document); | 187 ASSERT(document); |
| 188 HistoryItem* item = m_currentEntry->itemForFrame(frame); |
114 | 189 |
115 if (m_currentItem->isCurrentDocument(document) && document->isActive()) { | 190 if (item->isCurrentDocument(document) && document->isActive()) |
116 LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree
().uniqueName().string().utf8().data(), m_currentItem.get()); | 191 item->setDocumentState(document->formElementsState()); |
117 m_currentItem->setDocumentState(document->formElementsState()); | 192 |
| 193 if (!frame->view()) |
| 194 return; |
| 195 |
| 196 item->setScrollPoint(frame->view()->scrollPosition()); |
| 197 if (frame->isMainFrame() && !m_page->inspectorController().deviceEmulationEn
abled()) |
| 198 item->setPageScaleFactor(m_page->pageScaleFactor()); |
| 199 } |
| 200 |
| 201 void HistoryController::restoreDocumentState(Frame* frame) |
| 202 { |
| 203 HistoryItem* item = m_currentEntry ? m_currentEntry->itemForFrame(frame) : 0
; |
| 204 if (item && frame->loader().loadType() == FrameLoadTypeBackForward) |
| 205 frame->document()->setStateForNewFormElements(item->documentState()); |
| 206 } |
| 207 |
| 208 void HistoryController::goToEntry(PassOwnPtr<HistoryEntry> targetEntry) |
| 209 { |
| 210 ASSERT(m_sameDocumentLoadsInProgress.isEmpty()); |
| 211 ASSERT(m_differentDocumentLoadsInProgress.isEmpty()); |
| 212 |
| 213 m_provisionalEntry = targetEntry; |
| 214 recursiveGoToEntry(m_page->mainFrame()); |
| 215 |
| 216 if (m_differentDocumentLoadsInProgress.isEmpty()) { |
| 217 m_previousEntry = m_currentEntry.release(); |
| 218 m_currentEntry = m_provisionalEntry.release(); |
| 219 } else { |
| 220 m_page->mainFrame()->loader().stopAllLoaders(); |
118 } | 221 } |
119 | 222 |
120 if (!m_frame->view()) | 223 for (HistoryFrameLoadSet::iterator it = m_sameDocumentLoadsInProgress.begin(
); it != m_sameDocumentLoadsInProgress.end(); ++it) |
| 224 it->key->loader().loadHistoryItem(it->value, HistorySameDocumentLoad); |
| 225 for (HistoryFrameLoadSet::iterator it = m_differentDocumentLoadsInProgress.b
egin(); it != m_differentDocumentLoadsInProgress.end(); ++it) |
| 226 it->key->loader().loadHistoryItem(it->value, HistoryDifferentDocumentLoa
d); |
| 227 m_sameDocumentLoadsInProgress.clear(); |
| 228 m_differentDocumentLoadsInProgress.clear(); |
| 229 } |
| 230 |
| 231 void HistoryController::recursiveGoToEntry(Frame* frame) |
| 232 { |
| 233 HistoryItem* newItem = m_provisionalEntry->itemForFrame(frame); |
| 234 HistoryItem* oldItem = m_currentEntry ? m_currentEntry->itemForFrame(frame)
: 0; |
| 235 if (!newItem) |
121 return; | 236 return; |
122 | 237 |
123 m_currentItem->setScrollPoint(m_frame->view()->scrollPosition()); | 238 if (!oldItem || (newItem != oldItem && newItem->itemSequenceNumber() != oldI
tem->itemSequenceNumber())) { |
| 239 if (oldItem && newItem->documentSequenceNumber() == oldItem->documentSeq
uenceNumber()) |
| 240 m_sameDocumentLoadsInProgress.set(frame, newItem); |
| 241 else |
| 242 m_differentDocumentLoadsInProgress.set(frame, newItem); |
| 243 return; |
| 244 } |
124 | 245 |
125 Page* page = m_frame->page(); | 246 for (Frame* child = frame->tree().firstChild(); child; child = child->tree()
.nextSibling()) |
126 if (m_frame->isMainFrame() && !page->inspectorController().deviceEmulationEn
abled()) | 247 recursiveGoToEntry(child); |
127 m_currentItem->setPageScaleFactor(page->pageScaleFactor()); | |
128 } | 248 } |
129 | 249 |
130 void HistoryController::restoreDocumentState() | |
131 { | |
132 if (m_currentItem && m_frame->loader().loadType() == FrameLoadTypeBackForwar
d) | |
133 m_frame->document()->setStateForNewFormElements(m_currentItem->documentS
tate()); | |
134 } | |
135 | |
136 bool HistoryController::shouldStopLoadingForHistoryItem(HistoryItem* targetItem)
const | |
137 { | |
138 if (!m_currentItem) | |
139 return false; | |
140 // Don't abort the current load if we're navigating within the current docum
ent. | |
141 return !m_currentItem->shouldDoSameDocumentNavigationTo(targetItem); | |
142 } | |
143 | |
144 // Main funnel for navigating to a previous location (back/forward, non-search s
nap-back) | |
145 // This includes recursion to handle loading into framesets properly | |
146 void HistoryController::goToItem(HistoryItem* targetItem) | 250 void HistoryController::goToItem(HistoryItem* targetItem) |
147 { | 251 { |
148 ASSERT(!m_frame->tree().parent()); | |
149 | |
150 // shouldGoToHistoryItem is a private delegate method. This is needed to fix
: | |
151 // <rdar://problem/3951283> can view pages from the back/forward cache that
should be disallowed by Parental Controls | |
152 // Ultimately, history item navigations should go through the policy delegat
e. That's covered in: | |
153 // <rdar://problem/3979539> back/forward cache navigations should consult po
licy delegate | |
154 Page* page = m_frame->page(); | |
155 if (!page) | |
156 return; | |
157 if (m_defersLoading) { | 252 if (m_defersLoading) { |
158 m_deferredItem = targetItem; | 253 m_deferredItem = targetItem; |
159 return; | 254 return; |
160 } | 255 } |
161 | 256 |
162 clearProvisionalItemsInAllFrames(); | 257 OwnPtr<HistoryEntry> newEntry = HistoryEntry::create(targetItem); |
163 | 258 Deque<HistoryNode*> historyNodes; |
164 // First set the provisional item of any frames that are not actually naviga
ting. | 259 historyNodes.append(newEntry->rootHistoryNode()); |
165 // This must be done before trying to navigate the desired frame, because so
me | 260 while (!historyNodes.isEmpty()) { |
166 // navigations can commit immediately (such as about:blank). We must be sur
e that | 261 // For each item, read the children (if any) off the HistoryItem, |
167 // all frames have provisional items set before the commit. | 262 // create a new HistoryNode for each child and attach it, |
168 recursiveSetProvisionalItem(targetItem, m_currentItem.get()); | 263 // then clear the children on the HistoryItem. |
169 // Now that all other frames have provisional items, do the actual navigatio
n. | 264 HistoryNode* historyNode = historyNodes.takeFirst(); |
170 recursiveGoToItem(targetItem, m_currentItem.get()); | 265 const HistoryItemVector& children = historyNode->value()->children(); |
| 266 for (size_t i = 0; i < children.size(); i++) { |
| 267 HistoryNode* childHistoryNode = historyNode->addChild(children[i].ge
t()); |
| 268 historyNodes.append(childHistoryNode); |
| 269 } |
| 270 historyNode->value()->clearChildren(); |
| 271 } |
| 272 goToEntry(newEntry.release()); |
171 } | 273 } |
172 | 274 |
173 void HistoryController::setDefersLoading(bool defer) | 275 void HistoryController::setDefersLoading(bool defer) |
174 { | 276 { |
175 m_defersLoading = defer; | 277 m_defersLoading = defer; |
176 if (!defer && m_deferredItem) { | 278 if (!defer && m_deferredItem) { |
177 goToItem(m_deferredItem.get()); | 279 goToItem(m_deferredItem.get()); |
178 m_deferredItem = 0; | 280 m_deferredItem = 0; |
179 } | 281 } |
180 } | 282 } |
181 | 283 |
182 void HistoryController::clearProvisionalItemsInAllFrames() | |
183 { | |
184 for (RefPtr<Frame> frame = m_frame->page()->mainFrame(); frame; frame = fram
e->tree().traverseNext()) | |
185 frame->loader().history()->m_provisionalItem = 0; | |
186 } | |
187 | |
188 // There are 2 things you might think of as "history", all of which are handled
by these functions. | 284 // There are 2 things you might think of as "history", all of which are handled
by these functions. |
189 // | 285 // |
190 // 1) Back/forward: The m_currentItem is part of this mechanism. | 286 // 1) Back/forward: The m_currentItem is part of this mechanism. |
191 // 2) Global history: Handled by the client. | 287 // 2) Global history: Handled by the client. |
192 // | 288 // |
193 void HistoryController::updateForStandardLoad() | 289 void HistoryController::updateForStandardLoad(Frame* frame) |
194 { | 290 { |
195 LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s
", m_frame->loader().documentLoader()->url().string().ascii().data()); | 291 LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s
", frame->loader().documentLoader()->url().string().ascii().data()); |
196 createNewBackForwardItem(true); | 292 createNewBackForwardItem(frame, true); |
197 } | 293 } |
198 | 294 |
199 void HistoryController::updateForInitialLoadInChildFrame() | 295 void HistoryController::updateForInitialLoadInChildFrame(Frame* frame) |
200 { | 296 { |
201 Frame* parentFrame = m_frame->tree().parent(); | 297 ASSERT(frame->tree().parent()); |
202 if (parentFrame && parentFrame->loader().history()->m_currentItem) | 298 if (!m_currentEntry) |
203 parentFrame->loader().history()->m_currentItem->setChildItem(createItem(
)); | 299 return; |
| 300 if (HistoryNode* existingChildHistoryNode = m_currentEntry->historyNodeForFr
ame(frame)) |
| 301 existingChildHistoryNode->updateValue(createItem(frame)); |
| 302 else if (HistoryNode* parentHistoryNode = m_currentEntry->historyNodeForFram
e(frame->tree().parent())) |
| 303 parentHistoryNode->addChild(createItem(frame)); |
204 } | 304 } |
205 | 305 |
206 void HistoryController::updateForCommit() | 306 void HistoryController::updateForCommit(Frame* frame) |
207 { | 307 { |
208 FrameLoader& frameLoader = m_frame->loader(); | |
209 #if !LOG_DISABLED | 308 #if !LOG_DISABLED |
210 if (m_frame->document()) | 309 if (frame->document()) |
211 LOG(History, "WebCoreHistory: Updating History for commit in frame %s",
m_frame->document()->title().utf8().data()); | 310 LOG(History, "WebCoreHistory: Updating History for commit in frame %s",
frame->document()->title().utf8().data()); |
212 #endif | 311 #endif |
213 FrameLoadType type = frameLoader.loadType(); | 312 FrameLoadType type = frame->loader().loadType(); |
214 if (isBackForwardLoadType(type)) { | 313 if (isBackForwardLoadType(type) && m_provisionalEntry) { |
215 // Once committed, we want to use current item for saving DocState, and | 314 // Once committed, we want to use current item for saving DocState, and |
216 // the provisional item for restoring state. | 315 // the provisional item for restoring state. |
217 // Note previousItem must be set before we close the URL, which will | 316 // Note previousItem must be set before we close the URL, which will |
218 // happen when the data source is made non-provisional below | 317 // happen when the data source is made non-provisional below |
219 m_previousItem = m_currentItem; | 318 m_previousEntry = m_currentEntry.release(); |
220 ASSERT(m_provisionalItem); | 319 ASSERT(m_provisionalEntry); |
221 m_currentItem = m_provisionalItem; | 320 m_currentEntry = m_provisionalEntry.release(); |
222 m_provisionalItem = 0; | |
223 | |
224 // Tell all other frames in the tree to commit their provisional items a
nd | |
225 // restore their scroll position. We'll avoid this frame (which has alr
eady | |
226 // committed) and its children (which will be replaced). | |
227 Page* page = m_frame->page(); | |
228 ASSERT(page); | |
229 page->mainFrame()->loader().history()->recursiveUpdateForCommit(); | |
230 } else if (type != FrameLoadTypeRedirectWithLockedBackForwardList) { | 321 } else if (type != FrameLoadTypeRedirectWithLockedBackForwardList) { |
231 m_provisionalItem = 0; | 322 m_provisionalEntry.clear(); |
232 } | 323 } |
233 | 324 |
234 if (type == FrameLoadTypeStandard) | 325 if (type == FrameLoadTypeStandard) |
235 updateForStandardLoad(); | 326 updateForStandardLoad(frame); |
236 else if (type == FrameLoadTypeInitialInChildFrame) | 327 else if (type == FrameLoadTypeInitialInChildFrame) |
237 updateForInitialLoadInChildFrame(); | 328 updateForInitialLoadInChildFrame(frame); |
238 else | 329 else |
239 updateWithoutCreatingNewBackForwardItem(); | 330 updateWithoutCreatingNewBackForwardItem(frame); |
240 } | 331 } |
241 | 332 |
242 void HistoryController::recursiveUpdateForCommit() | 333 void HistoryController::updateForSameDocumentNavigation(Frame* frame) |
243 { | 334 { |
244 // The frame that navigated will now have a null provisional item. | 335 if (frame->document()->url().isEmpty()) |
245 // Ignore it and its children. | |
246 if (!m_provisionalItem) | |
247 return; | 336 return; |
248 | 337 if (HistoryItem* item = m_currentEntry->itemForFrame(frame)) |
249 // For each frame that already had the content the item requested (based on | 338 item->setURL(frame->document()->url()); |
250 // (a matching URL and frame tree snapshot), just restore the scroll positio
n. | |
251 // Save form state | |
252 if (m_currentItem && itemsAreClones(m_currentItem.get(), m_provisionalItem.g
et())) { | |
253 if (FrameView* view = m_frame->view()) | |
254 view->setWasScrolledByUser(false); | |
255 | |
256 // Now commit the provisional item | |
257 m_previousItem = m_currentItem; | |
258 m_currentItem = m_provisionalItem; | |
259 m_provisionalItem = 0; | |
260 | |
261 // Restore the scroll position (we choose to do this rather than going b
ack to the anchor point) | |
262 restoreScrollPositionAndViewState(); | |
263 } | |
264 | |
265 // Iterate over the rest of the tree | |
266 for (Frame* child = m_frame->tree().firstChild(); child; child = child->tree
().nextSibling()) | |
267 child->loader().history()->recursiveUpdateForCommit(); | |
268 } | 339 } |
269 | 340 |
270 void HistoryController::updateForSameDocumentNavigation() | 341 static PassRefPtr<HistoryItem> itemForExport(HistoryNode* historyNode) |
271 { | 342 { |
272 if (m_frame->document()->url().isEmpty()) | 343 RefPtr<HistoryItem> item = historyNode->value()->copy(); |
273 return; | 344 const Vector<OwnPtr<HistoryNode> >& childEntries = historyNode->children(); |
274 | 345 for (size_t i = 0; i < childEntries.size(); i++) |
275 Page* page = m_frame->page(); | 346 item->addChildItem(itemForExport(childEntries[i].get())); |
276 if (!page) | 347 return item; |
277 return; | |
278 | |
279 page->mainFrame()->loader().history()->recursiveUpdateForSameDocumentNavigat
ion(); | |
280 | |
281 if (m_currentItem) | |
282 m_currentItem->setURL(m_frame->document()->url()); | |
283 } | 348 } |
284 | 349 |
285 void HistoryController::recursiveUpdateForSameDocumentNavigation() | 350 PassRefPtr<HistoryItem> HistoryController::currentItemForExport(Frame* frame) |
286 { | 351 { |
287 // The frame that navigated will now have a null provisional item. | 352 if (!m_currentEntry) |
288 // Ignore it and its children. | 353 return 0; |
289 if (!m_provisionalItem) | 354 HistoryNode* historyNode = m_currentEntry->historyNodeForFrame(frame); |
290 return; | 355 return historyNode ? itemForExport(historyNode) : 0; |
291 | |
292 // The provisional item may represent a different pending navigation. | |
293 // Don't commit it if it isn't a same document navigation. | |
294 if (m_currentItem && !m_currentItem->shouldDoSameDocumentNavigationTo(m_prov
isionalItem.get())) | |
295 return; | |
296 | |
297 // Commit the provisional item. | |
298 m_previousItem = m_currentItem; | |
299 m_currentItem = m_provisionalItem; | |
300 m_provisionalItem = 0; | |
301 | |
302 // Iterate over the rest of the tree. | |
303 for (Frame* child = m_frame->tree().firstChild(); child; child = child->tree
().nextSibling()) | |
304 child->loader().history()->recursiveUpdateForSameDocumentNavigation(); | |
305 } | 356 } |
306 | 357 |
307 void HistoryController::setCurrentItem(HistoryItem* item) | 358 PassRefPtr<HistoryItem> HistoryController::previousItemForExport(Frame* frame) |
308 { | 359 { |
309 m_previousItem = m_currentItem; | 360 if (!m_previousEntry) |
310 m_currentItem = item; | 361 return 0; |
| 362 HistoryNode* historyNode = m_previousEntry->historyNodeForFrame(frame); |
| 363 return historyNode ? itemForExport(historyNode) : 0; |
311 } | 364 } |
312 | 365 |
313 bool HistoryController::currentItemShouldBeReplaced() const | 366 PassRefPtr<HistoryItem> HistoryController::provisionalItemForExport(Frame* frame
) |
| 367 { |
| 368 if (!m_provisionalEntry) |
| 369 return 0; |
| 370 HistoryNode* historyNode = m_provisionalEntry->historyNodeForFrame(frame); |
| 371 return historyNode ? itemForExport(historyNode) : 0; |
| 372 } |
| 373 |
| 374 HistoryItem* HistoryController::currentItem(Frame* frame) const |
| 375 { |
| 376 return m_currentEntry ? m_currentEntry->itemForFrame(frame) : 0; |
| 377 } |
| 378 |
| 379 bool HistoryController::currentItemShouldBeReplaced(Frame* frame) const |
314 { | 380 { |
315 // From the HTML5 spec for location.assign(): | 381 // From the HTML5 spec for location.assign(): |
316 // "If the browsing context's session history contains only one Document, | 382 // "If the browsing context's session history contains only one Document, |
317 // and that was the about:blank Document created when the browsing context | 383 // and that was the about:blank Document created when the browsing context |
318 // was created, then the navigation must be done with replacement enabled.
" | 384 // was created, then the navigation must be done with replacement enabled.
" |
319 return m_currentItem && !m_previousItem && equalIgnoringCase(m_currentItem->
urlString(), blankURL()); | 385 if (m_previousEntry && m_previousEntry->itemForFrame(frame)) |
| 386 return false; |
| 387 return equalIgnoringCase(frame->document()->url(), blankURL()); |
320 } | 388 } |
321 | 389 |
322 void HistoryController::setProvisionalItem(HistoryItem* item) | 390 HistoryItem* HistoryController::previousItem(Frame* frame) const |
323 { | 391 { |
324 m_provisionalItem = item; | 392 return m_previousEntry ? m_previousEntry->itemForFrame(frame) : 0; |
325 } | 393 } |
326 | 394 |
327 void HistoryController::initializeItem(HistoryItem* item) | 395 void HistoryController::clearProvisionalEntry() |
328 { | 396 { |
329 DocumentLoader* documentLoader = m_frame->loader().documentLoader(); | 397 m_provisionalEntry.clear(); |
| 398 } |
| 399 |
| 400 void HistoryController::initializeItem(HistoryItem* item, Frame* frame) |
| 401 { |
| 402 DocumentLoader* documentLoader = frame->loader().documentLoader(); |
330 ASSERT(documentLoader); | 403 ASSERT(documentLoader); |
331 | 404 |
332 KURL unreachableURL = documentLoader->unreachableURL(); | 405 KURL unreachableURL = documentLoader->unreachableURL(); |
333 | 406 |
334 KURL url; | 407 KURL url; |
335 KURL originalURL; | 408 KURL originalURL; |
336 | 409 |
337 if (!unreachableURL.isEmpty()) { | 410 if (!unreachableURL.isEmpty()) { |
338 url = unreachableURL; | 411 url = unreachableURL; |
339 originalURL = unreachableURL; | 412 originalURL = unreachableURL; |
340 } else { | 413 } else { |
341 url = documentLoader->url(); | 414 url = documentLoader->url(); |
342 originalURL = documentLoader->originalURL(); | 415 originalURL = documentLoader->originalURL(); |
343 } | 416 } |
344 | 417 |
345 // Frames that have never successfully loaded any content | 418 // Frames that have never successfully loaded any content |
346 // may have no URL at all. Currently our history code can't | 419 // may have no URL at all. Currently our history code can't |
347 // deal with such things, so we nip that in the bud here. | 420 // deal with such things, so we nip that in the bud here. |
348 // Later we may want to learn to live with nil for URL. | 421 // Later we may want to learn to live with nil for URL. |
349 // See bug 3368236 and related bugs for more information. | 422 // See bug 3368236 and related bugs for more information. |
350 if (url.isEmpty()) | 423 if (url.isEmpty()) |
351 url = blankURL(); | 424 url = blankURL(); |
352 if (originalURL.isEmpty()) | 425 if (originalURL.isEmpty()) |
353 originalURL = blankURL(); | 426 originalURL = blankURL(); |
354 | 427 |
355 Frame* parentFrame = m_frame->tree().parent(); | |
356 String parent = parentFrame ? parentFrame->tree().uniqueName() : ""; | |
357 | |
358 item->setURL(url); | 428 item->setURL(url); |
359 item->setTarget(m_frame->tree().uniqueName()); | 429 item->setTarget(frame->tree().uniqueName()); |
360 item->setTargetFrameID(m_frame->frameID()); | 430 item->setTargetFrameID(frame->frameID()); |
361 item->setOriginalURLString(originalURL.string()); | 431 item->setOriginalURLString(originalURL.string()); |
362 | 432 |
363 // Save form state if this is a POST | 433 // Save form state if this is a POST |
364 item->setFormInfoFromRequest(documentLoader->request()); | 434 item->setFormInfoFromRequest(documentLoader->request()); |
365 } | 435 } |
366 | 436 |
367 PassRefPtr<HistoryItem> HistoryController::createItem() | 437 PassRefPtr<HistoryItem> HistoryController::createItem(Frame* frame) |
368 { | 438 { |
369 RefPtr<HistoryItem> item = HistoryItem::create(); | 439 RefPtr<HistoryItem> item = HistoryItem::create(); |
370 initializeItem(item.get()); | 440 initializeItem(item.get(), frame); |
371 | |
372 // Set the item for which we will save document state | |
373 m_previousItem = m_currentItem; | |
374 m_currentItem = item; | |
375 | |
376 return item.release(); | 441 return item.release(); |
377 } | 442 } |
378 | 443 |
379 PassRefPtr<HistoryItem> HistoryController::createItemTree(Frame* targetFrame, bo
ol clipAtTarget) | 444 void HistoryController::createItemTree(Frame* targetFrame, bool clipAtTarget) |
380 { | 445 { |
381 RefPtr<HistoryItem> bfItem = createItem(); | 446 RefPtr<HistoryItem> newItem = createItem(targetFrame); |
382 | 447 if (!m_currentEntry) { |
383 if (!clipAtTarget || m_frame != targetFrame) { | 448 m_currentEntry = HistoryEntry::create(newItem.get()); |
384 // clipAtTarget is false for navigations within the same document, so | 449 } else { |
385 // we should copy the documentSequenceNumber over to the newly create | 450 HistoryItem* oldItem = m_currentEntry->itemForFrame(targetFrame); |
386 // item. Non-target items are just clones, and they should therefore | 451 if (!clipAtTarget) |
387 // preserve the same itemSequenceNumber. | 452 newItem->setDocumentSequenceNumber(oldItem->documentSequenceNumber()
); |
388 if (m_previousItem) { | 453 m_previousEntry = m_currentEntry.release(); |
389 if (m_frame != targetFrame) | 454 m_currentEntry = m_previousEntry->cloneAndReplace(newItem.get(), oldItem
, clipAtTarget, m_page); |
390 bfItem->setItemSequenceNumber(m_previousItem->itemSequenceNumber
()); | |
391 bfItem->setDocumentSequenceNumber(m_previousItem->documentSequenceNu
mber()); | |
392 } | |
393 | |
394 for (Frame* child = m_frame->tree().firstChild(); child; child = child->
tree().nextSibling()) { | |
395 // If the child is a frame corresponding to an <object> element that
never loaded, | |
396 // we don't want to create a history item, because that causes fallb
ack content | |
397 // to be ignored on reload. | |
398 FrameLoader& childLoader = child->loader(); | |
399 if (childLoader.stateMachine()->startedFirstRealLoad() || !child->ow
nerElement()->isObjectElement()) | |
400 bfItem->addChildItem(childLoader.history()->createItemTree(targe
tFrame, clipAtTarget)); | |
401 } | |
402 } | |
403 return bfItem; | |
404 } | |
405 | |
406 // The general idea here is to traverse the frame tree and the item tree in para
llel, | |
407 // tracking whether each frame already has the content the item requests. If th
ere is | |
408 // a match, we set the provisional item and recurse. Otherwise we will reload t
hat | |
409 // frame and all its kids in recursiveGoToItem. | |
410 void HistoryController::recursiveSetProvisionalItem(HistoryItem* item, HistoryIt
em* fromItem) | |
411 { | |
412 ASSERT(item); | |
413 | |
414 if (itemsAreClones(item, fromItem)) { | |
415 // Set provisional item, which will be committed in recursiveUpdateForCo
mmit. | |
416 m_provisionalItem = item; | |
417 | |
418 const HistoryItemVector& childItems = item->children(); | |
419 | |
420 int size = childItems.size(); | |
421 | |
422 for (int i = 0; i < size; ++i) { | |
423 String childFrameName = childItems[i]->target(); | |
424 HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFram
eName); | |
425 ASSERT(fromChildItem); | |
426 Frame* childFrame = m_frame->tree().child(childFrameName); | |
427 ASSERT(childFrame); | |
428 childFrame->loader().history()->recursiveSetProvisionalItem(childIte
ms[i].get(), fromChildItem); | |
429 } | |
430 } | 455 } |
431 } | 456 } |
432 | 457 |
433 // We now traverse the frame tree and item tree a second time, loading frames th
at | 458 void HistoryController::createNewBackForwardItem(Frame* frame, bool doClip) |
434 // do have the content the item requests. | |
435 void HistoryController::recursiveGoToItem(HistoryItem* item, HistoryItem* fromIt
em) | |
436 { | |
437 ASSERT(item); | |
438 | |
439 if (itemsAreClones(item, fromItem)) { | |
440 // Just iterate over the rest, looking for frames to navigate. | |
441 const HistoryItemVector& childItems = item->children(); | |
442 | |
443 int size = childItems.size(); | |
444 for (int i = 0; i < size; ++i) { | |
445 String childFrameName = childItems[i]->target(); | |
446 HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFram
eName); | |
447 ASSERT(fromChildItem); | |
448 Frame* childFrame = m_frame->tree().child(childFrameName); | |
449 ASSERT(childFrame); | |
450 childFrame->loader().history()->recursiveGoToItem(childItems[i].get(
), fromChildItem); | |
451 } | |
452 } else { | |
453 m_frame->loader().loadHistoryItem(item); | |
454 } | |
455 } | |
456 | |
457 bool HistoryController::itemsAreClones(HistoryItem* item1, HistoryItem* item2) c
onst | |
458 { | |
459 // If the item we're going to is a clone of the item we're at, then we do | |
460 // not need to load it again. The current frame tree and the frame tree | |
461 // snapshot in the item have to match. | |
462 // Note: Some clients treat a navigation to the current history item as | |
463 // a reload. Thus, if item1 and item2 are the same, we need to create a | |
464 // new document and should not consider them clones. | |
465 // (See http://webkit.org/b/35532 for details.) | |
466 return item1 | |
467 && item2 | |
468 && item1 != item2 | |
469 && item1->itemSequenceNumber() == item2->itemSequenceNumber() | |
470 && currentFramesMatchItem(item1) | |
471 && item2->hasSameFrames(item1); | |
472 } | |
473 | |
474 // Helper method that determines whether the current frame tree matches given hi
story item's. | |
475 bool HistoryController::currentFramesMatchItem(HistoryItem* item) const | |
476 { | |
477 if ((!m_frame->tree().uniqueName().isEmpty() || !item->target().isEmpty()) &
& m_frame->tree().uniqueName() != item->target()) | |
478 return false; | |
479 | |
480 const HistoryItemVector& childItems = item->children(); | |
481 if (childItems.size() != m_frame->tree().childCount()) | |
482 return false; | |
483 | |
484 unsigned size = childItems.size(); | |
485 for (unsigned i = 0; i < size; ++i) { | |
486 if (!m_frame->tree().child(childItems[i]->target())) | |
487 return false; | |
488 } | |
489 | |
490 return true; | |
491 } | |
492 | |
493 void HistoryController::createNewBackForwardItem(bool doClip) | |
494 { | 459 { |
495 // In the case of saving state about a page with frames, we store a tree of
items that mirrors the frame tree. | 460 // In the case of saving state about a page with frames, we store a tree of
items that mirrors the frame tree. |
496 // The item that was the target of the user's navigation is designated as th
e "targetItem". | 461 // The item that was the target of the user's navigation is designated as th
e "targetItem". |
497 // When this function is called with doClip=true we're able to create the wh
ole tree except for the target's children, | 462 // When this function is called with doClip=true we're able to create the wh
ole tree except for the target's children, |
498 // which will be loaded in the future. That part of the tree will be filled
out as the child loads are committed. | 463 // which will be loaded in the future. That part of the tree will be filled
out as the child loads are committed. |
| 464 if (!frame->loader().documentLoader()->isURLValidForNewHistoryEntry()) |
| 465 return; |
| 466 createItemTree(frame, doClip); |
| 467 } |
499 | 468 |
500 Page* page = m_frame->page(); | 469 void HistoryController::updateWithoutCreatingNewBackForwardItem(Frame* frame) |
501 if (!page) | 470 { |
| 471 if (!m_currentEntry || !m_currentEntry->itemForFrame(frame)) |
502 return; | 472 return; |
503 | 473 |
504 if (!m_frame->loader().documentLoader()->isURLValidForNewHistoryEntry()) | 474 DocumentLoader* documentLoader = frame->loader().documentLoader(); |
505 return; | |
506 | |
507 Frame* mainFrame = page->mainFrame(); | |
508 ASSERT(mainFrame); | |
509 | |
510 RefPtr<HistoryItem> topItem = mainFrame->loader().history()->createItemTree(
m_frame, doClip); | |
511 LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame
%s", topItem.get(), m_frame->loader().documentLoader()->url().string().ascii().d
ata()); | |
512 } | |
513 | |
514 void HistoryController::updateWithoutCreatingNewBackForwardItem() | |
515 { | |
516 if (!m_currentItem) | |
517 return; | |
518 | |
519 DocumentLoader* documentLoader = m_frame->loader().documentLoader(); | |
520 | 475 |
521 if (!documentLoader->unreachableURL().isEmpty()) | 476 if (!documentLoader->unreachableURL().isEmpty()) |
522 return; | 477 return; |
523 | 478 |
524 if (m_currentItem->url() != documentLoader->url()) { | 479 HistoryItem* item = m_currentEntry->itemForFrame(frame); |
525 m_currentItem->reset(); | 480 if (item->url() != documentLoader->url()) { |
526 initializeItem(m_currentItem.get()); | 481 item->reset(); |
| 482 initializeItem(item, frame); |
527 } else { | 483 } else { |
528 // Even if the final URL didn't change, the form data may have changed. | 484 // Even if the final URL didn't change, the form data may have changed. |
529 m_currentItem->setFormInfoFromRequest(documentLoader->request()); | 485 item->setFormInfoFromRequest(documentLoader->request()); |
530 } | 486 } |
531 } | 487 } |
532 | 488 |
533 void HistoryController::pushState(PassRefPtr<SerializedScriptValue> stateObject,
const String& urlString) | 489 void HistoryController::pushState(Frame* frame, PassRefPtr<SerializedScriptValue
> stateObject, const String& urlString) |
534 { | 490 { |
535 if (!m_currentItem) | 491 if (!m_currentEntry) |
536 return; | 492 return; |
537 | 493 |
538 Page* page = m_frame->page(); | 494 // Get a HistoryItem tree for the current frame tree, then override data to
reflect |
539 ASSERT(page); | |
540 | |
541 // Get a HistoryItem tree for the current frame tree. | |
542 RefPtr<HistoryItem> topItem = page->mainFrame()->loader().history()->createI
temTree(m_frame, false); | |
543 | |
544 // Override data in the current item (created by createItemTree) to reflect | |
545 // the pushState() arguments. | 495 // the pushState() arguments. |
546 m_currentItem->setStateObject(stateObject); | 496 createItemTree(frame, false); |
547 m_currentItem->setURLString(urlString); | 497 HistoryItem* item = m_currentEntry->itemForFrame(frame); |
| 498 if (!item) { |
| 499 updateForInitialLoadInChildFrame(frame); |
| 500 item = m_currentEntry->itemForFrame(frame); |
| 501 } |
| 502 item->setStateObject(stateObject); |
| 503 item->setURLString(urlString); |
548 } | 504 } |
549 | 505 |
550 void HistoryController::replaceState(PassRefPtr<SerializedScriptValue> stateObje
ct, const String& urlString) | 506 void HistoryController::replaceState(Frame* frame, PassRefPtr<SerializedScriptVa
lue> stateObject, const String& urlString) |
551 { | 507 { |
552 if (!m_currentItem) | 508 if (!m_currentEntry) |
| 509 return; |
| 510 |
| 511 HistoryItem* item = m_currentEntry->itemForFrame(frame); |
| 512 if (!item) |
553 return; | 513 return; |
554 | 514 |
555 if (!urlString.isEmpty()) | 515 if (!urlString.isEmpty()) |
556 m_currentItem->setURLString(urlString); | 516 item->setURLString(urlString); |
557 m_currentItem->setStateObject(stateObject); | 517 item->setStateObject(stateObject); |
558 m_currentItem->setFormData(0); | 518 item->setFormData(0); |
559 m_currentItem->setFormContentType(String()); | 519 item->setFormContentType(String()); |
560 | |
561 ASSERT(m_frame->page()); | |
562 } | 520 } |
563 | 521 |
564 } // namespace WebCore | 522 } // namespace WebCore |
OLD | NEW |