| 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 26 matching lines...) Expand all Loading... |
| 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 |
| OLD | NEW |