| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. | 
|  | 3 | * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) | 
|  | 4 | * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) | 
|  | 5 | * | 
|  | 6 | * Redistribution and use in source and binary forms, with or without | 
|  | 7 | * modification, are permitted provided that the following conditions | 
|  | 8 | * are met: | 
|  | 9 | * | 
|  | 10 | * 1.  Redistributions of source code must retain the above copyright | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 11 | *     notice, this list of conditions and the following disclaimer. | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 12 | * 2.  Redistributions in binary form must reproduce the above copyright | 
|  | 13 | *     notice, this list of conditions and the following disclaimer in the | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 14 | *     documentation and/or other materials provided with the distribution. | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 15 | * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of | 
|  | 16 | *     its contributors may be used to endorse or promote products derived | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 17 | *     from this software without specific prior written permission. | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 18 | * | 
|  | 19 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | 
|  | 20 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | 
|  | 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
|  | 22 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | 
|  | 23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | 
|  | 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 
|  | 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 
|  | 26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
|  | 28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | 29 | */ | 
|  | 30 |  | 
|  | 31 | #include "config.h" | 
|  | 32 | #include "core/loader/HistoryController.h" | 
|  | 33 |  | 
|  | 34 | #include "core/dom/Document.h" | 
|  | 35 | #include "core/history/BackForwardController.h" | 
|  | 36 | #include "core/history/HistoryItem.h" | 
|  | 37 | #include "core/loader/DocumentLoader.h" | 
|  | 38 | #include "core/loader/FrameLoader.h" | 
|  | 39 | #include "core/loader/FrameLoaderClient.h" | 
|  | 40 | #include "core/loader/FrameLoaderStateMachine.h" | 
|  | 41 | #include "core/page/Frame.h" | 
|  | 42 | #include "core/page/FrameTree.h" | 
|  | 43 | #include "core/page/FrameView.h" | 
|  | 44 | #include "core/page/Page.h" | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 45 | #include "core/page/scrolling/ScrollingCoordinator.h" | 
|  | 46 | #include "core/platform/Logging.h" | 
| Ben Murdoch | 591b958 | 2013-07-10 11:41:44 +0100 | [diff] [blame] | 47 | #include "wtf/text/CString.h" | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 48 |  | 
|  | 49 | namespace WebCore { | 
|  | 50 |  | 
|  | 51 | HistoryController::HistoryController(Frame* frame) | 
|  | 52 | : m_frame(frame) | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 53 | , m_defersLoading(false) | 
|  | 54 | { | 
|  | 55 | } | 
|  | 56 |  | 
|  | 57 | HistoryController::~HistoryController() | 
|  | 58 | { | 
|  | 59 | } | 
|  | 60 |  | 
|  | 61 | void HistoryController::saveScrollPositionAndViewStateToItem(HistoryItem* item) | 
|  | 62 | { | 
|  | 63 | if (!item || !m_frame->view()) | 
|  | 64 | return; | 
|  | 65 |  | 
|  | 66 | item->setScrollPoint(m_frame->view()->scrollPosition()); | 
|  | 67 |  | 
|  | 68 | Page* page = m_frame->page(); | 
|  | 69 | if (page && page->mainFrame() == m_frame) | 
|  | 70 | item->setPageScaleFactor(page->pageScaleFactor()); | 
|  | 71 | } | 
|  | 72 |  | 
|  | 73 | void HistoryController::clearScrollPositionAndViewState() | 
|  | 74 | { | 
|  | 75 | if (!m_currentItem) | 
|  | 76 | return; | 
|  | 77 |  | 
|  | 78 | m_currentItem->clearScrollPoint(); | 
|  | 79 | m_currentItem->setPageScaleFactor(0); | 
|  | 80 | } | 
|  | 81 |  | 
|  | 82 | /* | 
|  | 83 | There is a race condition between the layout and load completion that affects restoring the scroll position. | 
|  | 84 | We try to restore the scroll position at both the first layout and upon load completion. | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 85 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 86 | 1) If first layout happens before the load completes, we want to restore the scroll position then so that the | 
|  | 87 | first time we draw the page is already scrolled to the right place, instead of starting at the top and later | 
|  | 88 | jumping down.  It is possible that the old scroll position is past the part of the doc laid out so far, in | 
|  | 89 | which case the restore silent fails and we will fix it in when we try to restore on doc completion. | 
|  | 90 | 2) If the layout happens after the load completes, the attempt to restore at load completion time silently | 
|  | 91 | fails.  We then successfully restore it when the layout happens. | 
|  | 92 | */ | 
|  | 93 | void HistoryController::restoreScrollPositionAndViewState() | 
|  | 94 | { | 
|  | 95 | if (!m_frame->loader()->stateMachine()->committedFirstRealDocumentLoad()) | 
|  | 96 | return; | 
|  | 97 |  | 
|  | 98 | ASSERT(m_currentItem); | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 99 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 100 | // FIXME: As the ASSERT attests, it seems we should always have a currentItem here. | 
|  | 101 | // One counterexample is <rdar://problem/4917290> | 
|  | 102 | // For now, to cover this issue in release builds, there is no technical harm to returning | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 103 | // early and from a user standpoint - as in the above radar - the previous page load failed | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 104 | // so there *is* no scroll or view state to restore! | 
|  | 105 | if (!m_currentItem) | 
|  | 106 | return; | 
|  | 107 |  | 
|  | 108 | if (FrameView* view = m_frame->view()) { | 
|  | 109 | Page* page = m_frame->page(); | 
|  | 110 | if (page && page->mainFrame() == m_frame) { | 
|  | 111 | if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) | 
|  | 112 | scrollingCoordinator->frameViewRootLayerDidChange(view); | 
|  | 113 | } | 
|  | 114 |  | 
|  | 115 | if (!view->wasScrolledByUser()) { | 
|  | 116 | if (page && page->mainFrame() == m_frame && m_currentItem->pageScaleFactor()) | 
|  | 117 | page->setPageScaleFactor(m_currentItem->pageScaleFactor(), m_currentItem->scrollPoint()); | 
|  | 118 | else | 
| Torne (Richard Coles) | 5267f70 | 2013-06-11 10:57:24 +0100 | [diff] [blame] | 119 | view->setScrollPositionNonProgrammatically(m_currentItem->scrollPoint()); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 120 | } | 
|  | 121 | } | 
|  | 122 | } | 
|  | 123 |  | 
|  | 124 | void HistoryController::updateBackForwardListForFragmentScroll() | 
|  | 125 | { | 
|  | 126 | updateBackForwardListClippedAtTarget(false); | 
|  | 127 | } | 
|  | 128 |  | 
|  | 129 | void HistoryController::saveDocumentState() | 
|  | 130 | { | 
| Torne (Richard Coles) | f5e4ad5 | 2013-08-05 13:57:57 +0100 | [diff] [blame] | 131 | if (!m_currentItem) | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 132 | return; | 
|  | 133 |  | 
|  | 134 | Document* document = m_frame->document(); | 
|  | 135 | ASSERT(document); | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 136 |  | 
| Torne (Richard Coles) | f5e4ad5 | 2013-08-05 13:57:57 +0100 | [diff] [blame] | 137 | if (m_currentItem->isCurrentDocument(document) && document->attached()) { | 
|  | 138 | LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->uniqueName().string().utf8().data(), m_currentItem.get()); | 
|  | 139 | m_currentItem->setDocumentState(document->formElementsState()); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 140 | } | 
|  | 141 | } | 
|  | 142 |  | 
|  | 143 | // Walk the frame tree, telling all frames to save their form state into their current | 
|  | 144 | // history item. | 
|  | 145 | void HistoryController::saveDocumentAndScrollState() | 
|  | 146 | { | 
|  | 147 | for (Frame* frame = m_frame; frame; frame = frame->tree()->traverseNext(m_frame)) { | 
|  | 148 | frame->loader()->history()->saveDocumentState(); | 
|  | 149 | frame->loader()->history()->saveScrollPositionAndViewStateToItem(frame->loader()->history()->currentItem()); | 
|  | 150 | } | 
|  | 151 | } | 
|  | 152 |  | 
|  | 153 | static inline bool isAssociatedToRequestedHistoryItem(const HistoryItem* current, Frame* frame, const HistoryItem* requested) | 
|  | 154 | { | 
|  | 155 | if (requested == current) | 
|  | 156 | return true; | 
|  | 157 | if (requested) | 
|  | 158 | return false; | 
|  | 159 | while ((frame = frame->tree()->parent())) { | 
|  | 160 | requested = frame->loader()->requestedHistoryItem(); | 
|  | 161 | if (!requested) | 
|  | 162 | continue; | 
|  | 163 | if (requested->isAncestorOf(current)) | 
|  | 164 | return true; | 
|  | 165 | } | 
|  | 166 | return false; | 
|  | 167 | } | 
|  | 168 |  | 
|  | 169 | void HistoryController::restoreDocumentState() | 
|  | 170 | { | 
|  | 171 | Document* doc = m_frame->document(); | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 172 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 173 | HistoryItem* itemToRestore = 0; | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 174 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 175 | switch (m_frame->loader()->loadType()) { | 
|  | 176 | case FrameLoadTypeReload: | 
|  | 177 | case FrameLoadTypeReloadFromOrigin: | 
|  | 178 | case FrameLoadTypeSame: | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 179 | break; | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 180 | case FrameLoadTypeBackForward: | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 181 | case FrameLoadTypeRedirectWithLockedBackForwardList: | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 182 | case FrameLoadTypeInitialInChildFrame: | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 183 | case FrameLoadTypeStandard: | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 184 | itemToRestore = m_currentItem.get(); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 185 | } | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 186 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 187 | if (!itemToRestore) | 
|  | 188 | return; | 
|  | 189 | if (isAssociatedToRequestedHistoryItem(itemToRestore, m_frame, m_frame->loader()->requestedHistoryItem()) && !m_frame->loader()->documentLoader()->isClientRedirect()) { | 
|  | 190 | LOG(Loading, "WebCoreLoading %s: restoring form state from %p", m_frame->tree()->uniqueName().string().utf8().data(), itemToRestore); | 
|  | 191 | doc->setStateForNewFormElements(itemToRestore->documentState()); | 
|  | 192 | } | 
|  | 193 | } | 
|  | 194 |  | 
|  | 195 | bool HistoryController::shouldStopLoadingForHistoryItem(HistoryItem* targetItem) const | 
|  | 196 | { | 
|  | 197 | if (!m_currentItem) | 
|  | 198 | return false; | 
|  | 199 |  | 
|  | 200 | // Don't abort the current load if we're navigating within the current document. | 
|  | 201 | if (m_currentItem->shouldDoSameDocumentNavigationTo(targetItem)) | 
|  | 202 | return false; | 
|  | 203 |  | 
|  | 204 | return m_frame->loader()->client()->shouldStopLoadingForHistoryItem(targetItem); | 
|  | 205 | } | 
|  | 206 |  | 
|  | 207 | // Main funnel for navigating to a previous location (back/forward, non-search snap-back) | 
|  | 208 | // This includes recursion to handle loading into framesets properly | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 209 | void HistoryController::goToItem(HistoryItem* targetItem) | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 210 | { | 
|  | 211 | ASSERT(!m_frame->tree()->parent()); | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 212 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 213 | // shouldGoToHistoryItem is a private delegate method. This is needed to fix: | 
|  | 214 | // <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls | 
|  | 215 | // Ultimately, history item navigations should go through the policy delegate. That's covered in: | 
|  | 216 | // <rdar://problem/3979539> back/forward cache navigations should consult policy delegate | 
|  | 217 | Page* page = m_frame->page(); | 
|  | 218 | if (!page) | 
|  | 219 | return; | 
|  | 220 | if (!m_frame->loader()->client()->shouldGoToHistoryItem(targetItem)) | 
|  | 221 | return; | 
|  | 222 | if (m_defersLoading) { | 
|  | 223 | m_deferredItem = targetItem; | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 224 | return; | 
|  | 225 | } | 
|  | 226 |  | 
|  | 227 | // Set the BF cursor before commit, which lets the user quickly click back/forward again. | 
|  | 228 | // - plus, it only makes sense for the top level of the operation through the frametree, | 
|  | 229 | // as opposed to happening for some/one of the page commits that might happen soon | 
|  | 230 | RefPtr<HistoryItem> currentItem = page->backForward()->currentItem(); | 
|  | 231 | page->backForward()->setCurrentItem(targetItem); | 
|  | 232 |  | 
|  | 233 | // First set the provisional item of any frames that are not actually navigating. | 
|  | 234 | // This must be done before trying to navigate the desired frame, because some | 
|  | 235 | // navigations can commit immediately (such as about:blank).  We must be sure that | 
|  | 236 | // all frames have provisional items set before the commit. | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 237 | recursiveSetProvisionalItem(targetItem, currentItem.get()); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 238 | // Now that all other frames have provisional items, do the actual navigation. | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 239 | recursiveGoToItem(targetItem, currentItem.get()); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 240 | } | 
|  | 241 |  | 
|  | 242 | void HistoryController::setDefersLoading(bool defer) | 
|  | 243 | { | 
|  | 244 | m_defersLoading = defer; | 
|  | 245 | if (!defer && m_deferredItem) { | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 246 | goToItem(m_deferredItem.get()); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 247 | m_deferredItem = 0; | 
|  | 248 | } | 
|  | 249 | } | 
|  | 250 |  | 
|  | 251 | void HistoryController::updateForBackForwardNavigation() | 
|  | 252 | { | 
|  | 253 | #if !LOG_DISABLED | 
|  | 254 | if (m_frame->loader()->documentLoader()) | 
|  | 255 | LOG(History, "WebCoreHistory: Updating History for back/forward navigation in frame %s", m_frame->loader()->documentLoader()->title().string().utf8().data()); | 
|  | 256 | #endif | 
|  | 257 |  | 
| Torne (Richard Coles) | f5e4ad5 | 2013-08-05 13:57:57 +0100 | [diff] [blame] | 258 | saveScrollPositionAndViewStateToItem(m_previousItem.get()); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 259 |  | 
|  | 260 | // When traversing history, we may end up redirecting to a different URL | 
|  | 261 | // this time (e.g., due to cookies).  See http://webkit.org/b/49654. | 
|  | 262 | updateCurrentItem(); | 
|  | 263 | } | 
|  | 264 |  | 
|  | 265 | void HistoryController::updateForReload() | 
|  | 266 | { | 
|  | 267 | #if !LOG_DISABLED | 
|  | 268 | if (m_frame->loader()->documentLoader()) | 
|  | 269 | LOG(History, "WebCoreHistory: Updating History for reload in frame %s", m_frame->loader()->documentLoader()->title().string().utf8().data()); | 
|  | 270 | #endif | 
|  | 271 |  | 
|  | 272 | if (m_currentItem) { | 
|  | 273 | if (m_frame->loader()->loadType() == FrameLoadTypeReload || m_frame->loader()->loadType() == FrameLoadTypeReloadFromOrigin) | 
|  | 274 | saveScrollPositionAndViewStateToItem(m_currentItem.get()); | 
|  | 275 | } | 
|  | 276 |  | 
|  | 277 | // When reloading the page, we may end up redirecting to a different URL | 
|  | 278 | // this time (e.g., due to cookies).  See http://webkit.org/b/4072. | 
|  | 279 | updateCurrentItem(); | 
|  | 280 | } | 
|  | 281 |  | 
|  | 282 | // There are 2 things you might think of as "history", all of which are handled by these functions. | 
|  | 283 | // | 
|  | 284 | //     1) Back/forward: The m_currentItem is part of this mechanism. | 
|  | 285 | //     2) Global history: Handled by the client. | 
|  | 286 | // | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 287 | void HistoryController::updateForStandardLoad() | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 288 | { | 
|  | 289 | LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s", m_frame->loader()->documentLoader()->url().string().ascii().data()); | 
|  | 290 |  | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 291 | if (!m_frame->loader()->documentLoader()->urlForHistory().isEmpty()) | 
|  | 292 | updateBackForwardListClippedAtTarget(true); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 293 | } | 
|  | 294 |  | 
|  | 295 | void HistoryController::updateForRedirectWithLockedBackForwardList() | 
|  | 296 | { | 
|  | 297 | #if !LOG_DISABLED | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 298 | LOG(History, "WebCoreHistory: Updating History for redirect load in frame %s", m_frame->loader()->documentLoader()->title().string().utf8().data()); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 299 | #endif | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 300 |  | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 301 | if (!m_currentItem && !m_frame->tree()->parent()) { | 
|  | 302 | if (!m_frame->loader()->documentLoader()->urlForHistory().isEmpty()) | 
|  | 303 | updateBackForwardListClippedAtTarget(true); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 304 | } | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 305 | // The client redirect replaces the current history item. | 
|  | 306 | updateCurrentItem(); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 307 | } | 
|  | 308 |  | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 309 | void HistoryController::updateForInitialLoadInChildFrame() | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 310 | { | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 311 | Frame* parentFrame = m_frame->tree()->parent(); | 
|  | 312 | if (parentFrame && parentFrame->loader()->history()->m_currentItem) | 
|  | 313 | parentFrame->loader()->history()->m_currentItem->setChildItem(createItem()); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 314 | } | 
|  | 315 |  | 
|  | 316 | void HistoryController::updateForCommit() | 
|  | 317 | { | 
|  | 318 | FrameLoader* frameLoader = m_frame->loader(); | 
|  | 319 | #if !LOG_DISABLED | 
|  | 320 | if (frameLoader->documentLoader()) | 
|  | 321 | LOG(History, "WebCoreHistory: Updating History for commit in frame %s", frameLoader->documentLoader()->title().string().utf8().data()); | 
|  | 322 | #endif | 
|  | 323 | FrameLoadType type = frameLoader->loadType(); | 
| Ben Murdoch | fff8884 | 2013-07-30 15:20:09 +0100 | [diff] [blame] | 324 | if (isBackForwardLoadType(type) || (isReloadTypeWithProvisionalItem(type) && !frameLoader->documentLoader()->unreachableURL().isEmpty())) { | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 325 | // Once committed, we want to use current item for saving DocState, and | 
|  | 326 | // the provisional item for restoring state. | 
|  | 327 | // Note previousItem must be set before we close the URL, which will | 
|  | 328 | // happen when the data source is made non-provisional below | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 329 | m_previousItem = m_currentItem; | 
|  | 330 | ASSERT(m_provisionalItem); | 
|  | 331 | m_currentItem = m_provisionalItem; | 
|  | 332 | m_provisionalItem = 0; | 
|  | 333 |  | 
|  | 334 | // Tell all other frames in the tree to commit their provisional items and | 
|  | 335 | // restore their scroll position.  We'll avoid this frame (which has already | 
|  | 336 | // committed) and its children (which will be replaced). | 
|  | 337 | Page* page = m_frame->page(); | 
|  | 338 | ASSERT(page); | 
|  | 339 | page->mainFrame()->loader()->history()->recursiveUpdateForCommit(); | 
|  | 340 | } | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 341 |  | 
|  | 342 | switch (type) { | 
|  | 343 | case FrameLoadTypeBackForward: | 
|  | 344 | updateForBackForwardNavigation(); | 
|  | 345 | return; | 
|  | 346 | case FrameLoadTypeReload: | 
|  | 347 | case FrameLoadTypeReloadFromOrigin: | 
|  | 348 | case FrameLoadTypeSame: | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 349 | updateForReload(); | 
|  | 350 | return; | 
|  | 351 | case FrameLoadTypeStandard: | 
|  | 352 | updateForStandardLoad(); | 
|  | 353 | return; | 
|  | 354 | case FrameLoadTypeRedirectWithLockedBackForwardList: | 
|  | 355 | updateForRedirectWithLockedBackForwardList(); | 
|  | 356 | return; | 
|  | 357 | case FrameLoadTypeInitialInChildFrame: | 
|  | 358 | updateForInitialLoadInChildFrame(); | 
|  | 359 | return; | 
|  | 360 | default: | 
|  | 361 | ASSERT_NOT_REACHED(); | 
|  | 362 | } | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 363 | } | 
|  | 364 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 365 | bool HistoryController::isReloadTypeWithProvisionalItem(FrameLoadType type) | 
|  | 366 | { | 
|  | 367 | return (type == FrameLoadTypeReload || type == FrameLoadTypeReloadFromOrigin) && m_provisionalItem; | 
|  | 368 | } | 
|  | 369 |  | 
|  | 370 | void HistoryController::recursiveUpdateForCommit() | 
|  | 371 | { | 
|  | 372 | // The frame that navigated will now have a null provisional item. | 
|  | 373 | // Ignore it and its children. | 
|  | 374 | if (!m_provisionalItem) | 
|  | 375 | return; | 
|  | 376 |  | 
|  | 377 | // For each frame that already had the content the item requested (based on | 
|  | 378 | // (a matching URL and frame tree snapshot), just restore the scroll position. | 
| Torne (Richard Coles) | f5e4ad5 | 2013-08-05 13:57:57 +0100 | [diff] [blame] | 379 | // Save form state | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 380 | if (m_currentItem && itemsAreClones(m_currentItem.get(), m_provisionalItem.get())) { | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 381 | saveDocumentState(); | 
|  | 382 | saveScrollPositionAndViewStateToItem(m_currentItem.get()); | 
|  | 383 |  | 
|  | 384 | if (FrameView* view = m_frame->view()) | 
|  | 385 | view->setWasScrolledByUser(false); | 
|  | 386 |  | 
|  | 387 | // Now commit the provisional item | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 388 | m_previousItem = m_currentItem; | 
|  | 389 | m_currentItem = m_provisionalItem; | 
|  | 390 | m_provisionalItem = 0; | 
|  | 391 |  | 
|  | 392 | // Restore form state (works from currentItem) | 
|  | 393 | restoreDocumentState(); | 
|  | 394 |  | 
|  | 395 | // Restore the scroll position (we choose to do this rather than going back to the anchor point) | 
|  | 396 | restoreScrollPositionAndViewState(); | 
|  | 397 | } | 
|  | 398 |  | 
|  | 399 | // Iterate over the rest of the tree | 
|  | 400 | for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) | 
|  | 401 | child->loader()->history()->recursiveUpdateForCommit(); | 
|  | 402 | } | 
|  | 403 |  | 
|  | 404 | void HistoryController::updateForSameDocumentNavigation() | 
|  | 405 | { | 
|  | 406 | if (m_frame->document()->url().isEmpty()) | 
|  | 407 | return; | 
|  | 408 |  | 
|  | 409 | Page* page = m_frame->page(); | 
|  | 410 | if (!page) | 
|  | 411 | return; | 
|  | 412 |  | 
|  | 413 | page->mainFrame()->loader()->history()->recursiveUpdateForSameDocumentNavigation(); | 
|  | 414 |  | 
|  | 415 | if (m_currentItem) | 
|  | 416 | m_currentItem->setURL(m_frame->document()->url()); | 
|  | 417 | } | 
|  | 418 |  | 
|  | 419 | void HistoryController::recursiveUpdateForSameDocumentNavigation() | 
|  | 420 | { | 
|  | 421 | // The frame that navigated will now have a null provisional item. | 
|  | 422 | // Ignore it and its children. | 
|  | 423 | if (!m_provisionalItem) | 
|  | 424 | return; | 
|  | 425 |  | 
|  | 426 | // The provisional item may represent a different pending navigation. | 
|  | 427 | // Don't commit it if it isn't a same document navigation. | 
|  | 428 | if (m_currentItem && !m_currentItem->shouldDoSameDocumentNavigationTo(m_provisionalItem.get())) | 
|  | 429 | return; | 
|  | 430 |  | 
|  | 431 | // Commit the provisional item. | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 432 | m_previousItem = m_currentItem; | 
|  | 433 | m_currentItem = m_provisionalItem; | 
|  | 434 | m_provisionalItem = 0; | 
|  | 435 |  | 
|  | 436 | // Iterate over the rest of the tree. | 
|  | 437 | for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) | 
|  | 438 | child->loader()->history()->recursiveUpdateForSameDocumentNavigation(); | 
|  | 439 | } | 
|  | 440 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 441 | void HistoryController::setCurrentItem(HistoryItem* item) | 
|  | 442 | { | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 443 | m_previousItem = m_currentItem; | 
|  | 444 | m_currentItem = item; | 
|  | 445 | } | 
|  | 446 |  | 
|  | 447 | void HistoryController::setCurrentItemTitle(const StringWithDirection& title) | 
|  | 448 | { | 
|  | 449 | if (m_currentItem) | 
|  | 450 | // FIXME: make use of title.direction() as well. | 
|  | 451 | m_currentItem->setTitle(title.string()); | 
|  | 452 | } | 
|  | 453 |  | 
|  | 454 | bool HistoryController::currentItemShouldBeReplaced() const | 
|  | 455 | { | 
|  | 456 | // From the HTML5 spec for location.assign(): | 
|  | 457 | //  "If the browsing context's session history contains only one Document, | 
|  | 458 | //   and that was the about:blank Document created when the browsing context | 
|  | 459 | //   was created, then the navigation must be done with replacement enabled." | 
|  | 460 | return m_currentItem && !m_previousItem && equalIgnoringCase(m_currentItem->urlString(), blankURL()); | 
|  | 461 | } | 
|  | 462 |  | 
|  | 463 | void HistoryController::setProvisionalItem(HistoryItem* item) | 
|  | 464 | { | 
|  | 465 | m_provisionalItem = item; | 
|  | 466 | } | 
|  | 467 |  | 
|  | 468 | void HistoryController::initializeItem(HistoryItem* item) | 
|  | 469 | { | 
|  | 470 | DocumentLoader* documentLoader = m_frame->loader()->documentLoader(); | 
|  | 471 | ASSERT(documentLoader); | 
|  | 472 |  | 
|  | 473 | KURL unreachableURL = documentLoader->unreachableURL(); | 
|  | 474 |  | 
|  | 475 | KURL url; | 
|  | 476 | KURL originalURL; | 
|  | 477 |  | 
|  | 478 | if (!unreachableURL.isEmpty()) { | 
|  | 479 | url = unreachableURL; | 
|  | 480 | originalURL = unreachableURL; | 
|  | 481 | } else { | 
|  | 482 | url = documentLoader->url(); | 
|  | 483 | originalURL = documentLoader->originalURL(); | 
|  | 484 | } | 
|  | 485 |  | 
|  | 486 | // Frames that have never successfully loaded any content | 
|  | 487 | // may have no URL at all. Currently our history code can't | 
|  | 488 | // deal with such things, so we nip that in the bud here. | 
|  | 489 | // Later we may want to learn to live with nil for URL. | 
|  | 490 | // See bug 3368236 and related bugs for more information. | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 491 | if (url.isEmpty()) | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 492 | url = blankURL(); | 
|  | 493 | if (originalURL.isEmpty()) | 
|  | 494 | originalURL = blankURL(); | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 495 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 496 | Frame* parentFrame = m_frame->tree()->parent(); | 
|  | 497 | String parent = parentFrame ? parentFrame->tree()->uniqueName() : ""; | 
|  | 498 | StringWithDirection title = documentLoader->title(); | 
|  | 499 |  | 
|  | 500 | item->setURL(url); | 
|  | 501 | item->setTarget(m_frame->tree()->uniqueName()); | 
|  | 502 | item->setParent(parent); | 
|  | 503 | // FIXME: should store title directionality in history as well. | 
|  | 504 | item->setTitle(title.string()); | 
|  | 505 | item->setOriginalURLString(originalURL.string()); | 
|  | 506 |  | 
|  | 507 | // Save form state if this is a POST | 
|  | 508 | item->setFormInfoFromRequest(documentLoader->request()); | 
|  | 509 | } | 
|  | 510 |  | 
|  | 511 | PassRefPtr<HistoryItem> HistoryController::createItem() | 
|  | 512 | { | 
|  | 513 | RefPtr<HistoryItem> item = HistoryItem::create(); | 
|  | 514 | initializeItem(item.get()); | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 515 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 516 | // Set the item for which we will save document state | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 517 | m_previousItem = m_currentItem; | 
|  | 518 | m_currentItem = item; | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 519 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 520 | return item.release(); | 
|  | 521 | } | 
|  | 522 |  | 
|  | 523 | PassRefPtr<HistoryItem> HistoryController::createItemTree(Frame* targetFrame, bool clipAtTarget) | 
|  | 524 | { | 
|  | 525 | RefPtr<HistoryItem> bfItem = createItem(); | 
| Torne (Richard Coles) | f5e4ad5 | 2013-08-05 13:57:57 +0100 | [diff] [blame] | 526 | saveScrollPositionAndViewStateToItem(m_previousItem.get()); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 527 |  | 
|  | 528 | if (!clipAtTarget || m_frame != targetFrame) { | 
|  | 529 | // save frame state for items that aren't loading (khtml doesn't save those) | 
|  | 530 | saveDocumentState(); | 
|  | 531 |  | 
|  | 532 | // clipAtTarget is false for navigations within the same document, so | 
|  | 533 | // we should copy the documentSequenceNumber over to the newly create | 
|  | 534 | // item.  Non-target items are just clones, and they should therefore | 
|  | 535 | // preserve the same itemSequenceNumber. | 
|  | 536 | if (m_previousItem) { | 
|  | 537 | if (m_frame != targetFrame) | 
|  | 538 | bfItem->setItemSequenceNumber(m_previousItem->itemSequenceNumber()); | 
|  | 539 | bfItem->setDocumentSequenceNumber(m_previousItem->documentSequenceNumber()); | 
|  | 540 | } | 
|  | 541 |  | 
|  | 542 | for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) { | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 543 | // If the child is a frame corresponding to an <object> element that never loaded, | 
|  | 544 | // we don't want to create a history item, because that causes fallback content | 
|  | 545 | // to be ignored on reload. | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 546 | FrameLoader* childLoader = child->loader(); | 
|  | 547 | if (childLoader->stateMachine()->startedFirstRealLoad() || !childLoader->isHostedByObjectElement()) | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 548 | bfItem->addChildItem(childLoader->history()->createItemTree(targetFrame, clipAtTarget)); | 
|  | 549 | } | 
|  | 550 | } | 
|  | 551 | // FIXME: Eliminate the isTargetItem flag in favor of itemSequenceNumber. | 
|  | 552 | if (m_frame == targetFrame) | 
|  | 553 | bfItem->setIsTargetItem(true); | 
|  | 554 | return bfItem; | 
|  | 555 | } | 
|  | 556 |  | 
|  | 557 | // The general idea here is to traverse the frame tree and the item tree in parallel, | 
|  | 558 | // tracking whether each frame already has the content the item requests.  If there is | 
|  | 559 | // a match, we set the provisional item and recurse.  Otherwise we will reload that | 
|  | 560 | // frame and all its kids in recursiveGoToItem. | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 561 | void HistoryController::recursiveSetProvisionalItem(HistoryItem* item, HistoryItem* fromItem) | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 562 | { | 
|  | 563 | ASSERT(item); | 
|  | 564 |  | 
|  | 565 | if (itemsAreClones(item, fromItem)) { | 
|  | 566 | // Set provisional item, which will be committed in recursiveUpdateForCommit. | 
|  | 567 | m_provisionalItem = item; | 
|  | 568 |  | 
|  | 569 | const HistoryItemVector& childItems = item->children(); | 
|  | 570 |  | 
|  | 571 | int size = childItems.size(); | 
|  | 572 |  | 
|  | 573 | for (int i = 0; i < size; ++i) { | 
|  | 574 | String childFrameName = childItems[i]->target(); | 
|  | 575 | HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName); | 
|  | 576 | ASSERT(fromChildItem); | 
|  | 577 | Frame* childFrame = m_frame->tree()->child(childFrameName); | 
|  | 578 | ASSERT(childFrame); | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 579 | childFrame->loader()->history()->recursiveSetProvisionalItem(childItems[i].get(), fromChildItem); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 580 | } | 
|  | 581 | } | 
|  | 582 | } | 
|  | 583 |  | 
|  | 584 | // We now traverse the frame tree and item tree a second time, loading frames that | 
|  | 585 | // do have the content the item requests. | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 586 | void HistoryController::recursiveGoToItem(HistoryItem* item, HistoryItem* fromItem) | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 587 | { | 
|  | 588 | ASSERT(item); | 
|  | 589 |  | 
|  | 590 | if (itemsAreClones(item, fromItem)) { | 
|  | 591 | // Just iterate over the rest, looking for frames to navigate. | 
|  | 592 | const HistoryItemVector& childItems = item->children(); | 
|  | 593 |  | 
|  | 594 | int size = childItems.size(); | 
|  | 595 | for (int i = 0; i < size; ++i) { | 
|  | 596 | String childFrameName = childItems[i]->target(); | 
|  | 597 | HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName); | 
|  | 598 | ASSERT(fromChildItem); | 
|  | 599 | Frame* childFrame = m_frame->tree()->child(childFrameName); | 
|  | 600 | ASSERT(childFrame); | 
| Torne (Richard Coles) | 81a5157 | 2013-05-13 16:52:28 +0100 | [diff] [blame] | 601 | childFrame->loader()->history()->recursiveGoToItem(childItems[i].get(), fromChildItem); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 602 | } | 
|  | 603 | } else { | 
| Ben Murdoch | 7757ec2 | 2013-07-23 11:17:36 +0100 | [diff] [blame] | 604 | m_frame->loader()->loadHistoryItem(item); | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 605 | } | 
|  | 606 | } | 
|  | 607 |  | 
|  | 608 | bool HistoryController::itemsAreClones(HistoryItem* item1, HistoryItem* item2) const | 
|  | 609 | { | 
|  | 610 | // If the item we're going to is a clone of the item we're at, then we do | 
|  | 611 | // not need to load it again.  The current frame tree and the frame tree | 
|  | 612 | // snapshot in the item have to match. | 
|  | 613 | // Note: Some clients treat a navigation to the current history item as | 
|  | 614 | // a reload.  Thus, if item1 and item2 are the same, we need to create a | 
|  | 615 | // new document and should not consider them clones. | 
|  | 616 | // (See http://webkit.org/b/35532 for details.) | 
|  | 617 | return item1 | 
|  | 618 | && item2 | 
|  | 619 | && item1 != item2 | 
|  | 620 | && item1->itemSequenceNumber() == item2->itemSequenceNumber() | 
|  | 621 | && currentFramesMatchItem(item1) | 
|  | 622 | && item2->hasSameFrames(item1); | 
|  | 623 | } | 
|  | 624 |  | 
|  | 625 | // Helper method that determines whether the current frame tree matches given history item's. | 
|  | 626 | bool HistoryController::currentFramesMatchItem(HistoryItem* item) const | 
|  | 627 | { | 
|  | 628 | if ((!m_frame->tree()->uniqueName().isEmpty() || !item->target().isEmpty()) && m_frame->tree()->uniqueName() != item->target()) | 
|  | 629 | return false; | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 630 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 631 | const HistoryItemVector& childItems = item->children(); | 
|  | 632 | if (childItems.size() != m_frame->tree()->childCount()) | 
|  | 633 | return false; | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 634 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 635 | unsigned size = childItems.size(); | 
|  | 636 | for (unsigned i = 0; i < size; ++i) { | 
|  | 637 | if (!m_frame->tree()->child(childItems[i]->target())) | 
|  | 638 | return false; | 
|  | 639 | } | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 640 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 641 | return true; | 
|  | 642 | } | 
|  | 643 |  | 
|  | 644 | void HistoryController::updateBackForwardListClippedAtTarget(bool doClip) | 
|  | 645 | { | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 646 | // In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree. | 
|  | 647 | // The item that was the target of the user's navigation is designated as the "targetItem". | 
|  | 648 | // When this function is called with doClip=true we're able to create the whole tree except for the target's children, | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 649 | // which will be loaded in the future. That part of the tree will be filled out as the child loads are committed. | 
|  | 650 |  | 
|  | 651 | Page* page = m_frame->page(); | 
|  | 652 | if (!page) | 
|  | 653 | return; | 
|  | 654 |  | 
|  | 655 | if (m_frame->loader()->documentLoader()->urlForHistory().isEmpty()) | 
|  | 656 | return; | 
|  | 657 |  | 
|  | 658 | Frame* mainFrame = page->mainFrame(); | 
|  | 659 | ASSERT(mainFrame); | 
|  | 660 |  | 
|  | 661 | RefPtr<HistoryItem> topItem = mainFrame->loader()->history()->createItemTree(m_frame, doClip); | 
|  | 662 | LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", topItem.get(), m_frame->loader()->documentLoader()->url().string().ascii().data()); | 
|  | 663 | page->backForward()->addItem(topItem.release()); | 
|  | 664 | } | 
|  | 665 |  | 
|  | 666 | void HistoryController::updateCurrentItem() | 
|  | 667 | { | 
|  | 668 | if (!m_currentItem) | 
|  | 669 | return; | 
|  | 670 |  | 
|  | 671 | DocumentLoader* documentLoader = m_frame->loader()->documentLoader(); | 
|  | 672 |  | 
|  | 673 | if (!documentLoader->unreachableURL().isEmpty()) | 
|  | 674 | return; | 
|  | 675 |  | 
|  | 676 | if (m_currentItem->url() != documentLoader->url()) { | 
|  | 677 | // We ended up on a completely different URL this time, so the HistoryItem | 
|  | 678 | // needs to be re-initialized.  Preserve the isTargetItem flag as it is a | 
|  | 679 | // property of how this HistoryItem was originally created and is not | 
|  | 680 | // dependent on the document. | 
|  | 681 | bool isTargetItem = m_currentItem->isTargetItem(); | 
|  | 682 | m_currentItem->reset(); | 
|  | 683 | initializeItem(m_currentItem.get()); | 
|  | 684 | m_currentItem->setIsTargetItem(isTargetItem); | 
|  | 685 | } else { | 
|  | 686 | // Even if the final URL didn't change, the form data may have changed. | 
|  | 687 | m_currentItem->setFormInfoFromRequest(documentLoader->request()); | 
|  | 688 | } | 
|  | 689 | } | 
|  | 690 |  | 
|  | 691 | void HistoryController::pushState(PassRefPtr<SerializedScriptValue> stateObject, const String& title, const String& urlString) | 
|  | 692 | { | 
|  | 693 | if (!m_currentItem) | 
|  | 694 | return; | 
|  | 695 |  | 
|  | 696 | Page* page = m_frame->page(); | 
|  | 697 | ASSERT(page); | 
|  | 698 |  | 
|  | 699 | // Get a HistoryItem tree for the current frame tree. | 
|  | 700 | RefPtr<HistoryItem> topItem = page->mainFrame()->loader()->history()->createItemTree(m_frame, false); | 
| Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 701 |  | 
| Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 702 | // Override data in the current item (created by createItemTree) to reflect | 
|  | 703 | // the pushState() arguments. | 
|  | 704 | m_currentItem->setTitle(title); | 
|  | 705 | m_currentItem->setStateObject(stateObject); | 
|  | 706 | m_currentItem->setURLString(urlString); | 
|  | 707 |  | 
|  | 708 | page->backForward()->addItem(topItem.release()); | 
|  | 709 | } | 
|  | 710 |  | 
|  | 711 | void HistoryController::replaceState(PassRefPtr<SerializedScriptValue> stateObject, const String& title, const String& urlString) | 
|  | 712 | { | 
|  | 713 | if (!m_currentItem) | 
|  | 714 | return; | 
|  | 715 |  | 
|  | 716 | if (!urlString.isEmpty()) | 
|  | 717 | m_currentItem->setURLString(urlString); | 
|  | 718 | m_currentItem->setTitle(title); | 
|  | 719 | m_currentItem->setStateObject(stateObject); | 
|  | 720 | m_currentItem->setFormData(0); | 
|  | 721 | m_currentItem->setFormContentType(String()); | 
|  | 722 |  | 
|  | 723 | ASSERT(m_frame->page()); | 
|  | 724 | } | 
|  | 725 |  | 
|  | 726 | } // namespace WebCore |