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

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

Powered by Google App Engine
This is Rietveld 408576698