Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(159)

Side by Side Diff: Source/core/loader/HistoryController.cpp

Issue 28983004: Split the frame tree logic out of HistoryItem (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/core/loader/HistoryController.h ('k') | Source/core/loader/NavigationScheduler.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
OLDNEW
« no previous file with comments | « Source/core/loader/HistoryController.h ('k') | Source/core/loader/NavigationScheduler.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698